Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions apps/cinc/cmd/cookbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,10 @@ cinc cookbook upload nginx`,
if err != nil {
return err
}
// ReadVersion only guards against a version computed in Ruby,
// which would otherwise upload as Chef's default 0.0.0. The
// name and version uploaded are the ones cinc-api reads from
// the metadata, normalized as Chef does ("1.2" is 1.2.0), and
// the name is the metadata's, not the directory's.
if _, err := localcookbook.ReadVersion(dir); err != nil {
return err
}
cb, err := localcookbook.UploadableFromDir(dir, "")
// The name and version uploaded are the ones the metadata
// declares, normalized as Chef does ("1.2" is 1.2.0), and the
// name is the metadata's, not the directory's.
cb, err := localcookbook.Load(dir, false)
if err != nil {
return err
}
Expand Down
41 changes: 40 additions & 1 deletion apps/cinc/cmd/cookbook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"
"path/filepath"
"slices"
"strings"
"testing"

cinc "github.com/cinc-project/cinc-api"
Expand Down Expand Up @@ -297,14 +298,52 @@ func TestCookbookUploadRequiresLiteralVersion(t *testing.T) {
root.SetOut(io.Discard)
root.SetErr(io.Discard)
root.SetArgs([]string{"cookbook", "upload", "nginx", "--cookbook-path", dir, "--config", cfgPath})
if err := root.Execute(); err == nil {
err := root.Execute()
if err == nil {
t.Fatal("upload of a cookbook with a computed version succeeded, want an error")
}
if !strings.Contains(err.Error(), "metadata.json") || !strings.Contains(err.Error(), "version '1.2.3'") {
t.Errorf("error = %q, want it to suggest a literal version or a metadata.json", err)
}
if len(manifests) != 0 {
t.Errorf("manifest PUTs = %v, want none", manifests)
}
}

// TestCookbookUploadFromInsideTheCookbook uploads the cookbook the user is
// standing in, whose metadata.rb sets no name. Like knife, it goes up under
// the directory's name.
func TestCookbookUploadFromInsideTheCookbook(t *testing.T) {
cbDir := filepath.Join(t.TempDir(), "nginx")
if err := os.MkdirAll(filepath.Join(cbDir, "recipes"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(cbDir, "metadata.rb"), []byte("version '1.0.0'\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(cbDir, "recipes", "default.rb"), []byte("package 'nginx'\n"), 0o644); err != nil {
t.Fatal(err)
}
var manifests []string
srv := uploadAnythingServer(t, &manifests)
cfgPath := filepath.Join(t.TempDir(), "credentials")
cfg := fmt.Sprintf("[default]\ncinc_server_url = \"%s/organizations/acme\"\nclient_name = \"tim\"\nclient_key = %q\n",
srv.URL, writeTestKey(t))
if err := os.WriteFile(cfgPath, []byte(cfg), 0o600); err != nil {
t.Fatal(err)
}
t.Chdir(cbDir)
root := newRootCmd()
root.SetOut(io.Discard)
root.SetArgs([]string{"cookbook", "upload", "nginx", "--config", cfgPath})
if err := root.Execute(); err != nil {
t.Fatalf("cinc cookbook upload: %v", err)
}
if want := []string{"/organizations/acme/cookbooks/nginx/1.0.0"}; !slices.Equal(manifests, want) {
t.Errorf("manifest PUTs = %v, want %v", manifests, want)
}
}

func TestCookbookShowCommandEndToEnd(t *testing.T) {
manifest := cinc.Cookbook{
CookbookName: "nginx",
Expand Down
6 changes: 5 additions & 1 deletion apps/cinc/cmd/supermarket_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ func buildInstallTarball(t *testing.T, name, version string) []byte {
if err := os.WriteFile(filepath.Join(dir, "recipes", "default.rb"), []byte("package '"+name+"'\n"), 0o644); err != nil {
t.Fatal(err)
}
archive, err := localcookbook.BuildArchive(dir, name)
cb, err := localcookbook.Load(dir, false)
if err != nil {
t.Fatalf("Load: %v", err)
}
archive, err := localcookbook.BuildArchive(cb, nil)
if err != nil {
t.Fatalf("BuildArchive: %v", err)
}
Expand Down
23 changes: 15 additions & 8 deletions cli/cookbook/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@

## Gotchas

- **Two places decide what counts as a cookbook file.** `archiveEntries` here
(for uploads) and `copyTree` in `cli/policyfile` (for export bundles) walk a
cookbook independently. Changing the rules in one without the other makes
`cinc cookbook upload` and `cinc policyfile export` disagree about the same
directory. Change both, and test both.
- **cinc-api decides what a cookbook is, in one place.** Which files belong
to a cookbook (Chef's loader rules: chefignore, root dot-directories,
chef-zero's sentinel, symlinks) and what its metadata says come from
`cinc.LocalCookbookFromDir`, its `Files()` and `Identifiers()`, and
`cinc.LoadCookbookMetadata`. `cinc cookbook upload`, `cinc supermarket
share`, `cinc policy export`/`push` and the resolver's identifiers all use
them, so they agree about the same directory. Don't walk a cookbook
directory or parse metadata.rb here; if the rules are wrong, fix cinc-api.
- **Load cookbooks through `Load`.** It passes an absolute path, so a cookbook
whose metadata sets no name is named after its directory rather than `.`
when the user is standing in it, and it turns a computed metadata.rb
version into a message the user can act on.

## Test seams

`maxExtractedFileBytes` and `maxExtractedArchiveBytes` in `extract.go` are the
extraction caps. Tests shrink them rather than building giant fixtures; restore
them with `t.Cleanup` or a deferred restore.
Tarball extraction lives in `cli/internal/tarball`, which owns the extraction
caps (`maxFileBytes`, `maxArchiveBytes`). Its tests shrink them; callers'
tests don't need to.
Loading
Loading