From 74dc5c55c13d6d929ca61a0198d6300ae4f5b323 Mon Sep 17 00:00:00 2001 From: Ramkumar Vengadakrishnan Date: Wed, 20 Mar 2024 09:42:43 -0500 Subject: [PATCH 1/6] fix: Updated logic on when to bake Kilnfile arguments - Bake arguments from kilnfile only when the stub releases flag is not set. - This is causing TAS PRs checks to fail as currently for stub releases arguments are not needed. Authored-by: Ramkumar Vengadakrishnan --- internal/commands/bake.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/commands/bake.go b/internal/commands/bake.go index 10871584..a4dc469d 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -447,8 +447,10 @@ func (b Bake) Execute(args []string) error { // TODO remove when stemcell tarball is deprecated stemcellManifest, err = b.stemcell.FromTarball(b.Options.StemcellTarball) } else if b.Options.Kilnfile != "" { - if err := BakeArgumentsFromKilnfileConfiguration(&b.Options, templateVariables); err != nil { - return fmt.Errorf("failed to parse releases: %s", err) + if !b.Options.StubReleases { + if err := BakeArgumentsFromKilnfileConfiguration(&b.Options, templateVariables); err != nil { + return fmt.Errorf("failed to parse releases: %s", err) + } } templateVariables, err = b.templateVariables.FromPathsAndPairs(b.Options.VariableFiles, b.Options.Variables) if err != nil { From af985c5fe454f2fd3339a5525120408d26f754ff Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Wed, 20 Mar 2024 17:08:04 -0700 Subject: [PATCH 2/6] fix(bake): skip variable interpolation when loading Kilinfiles we have a variable bootstrapping problem when we try to load variables before interpolating Kilnfiles Ram and I noticed the stemcell service does not do interpolation so we can also skip it for bake config this also adds a bit more testing for bake configs --- internal/acceptance/bake/bake_test.go | 2 +- internal/commands/bake.go | 42 +++++----- internal/commands/bake_test.go | 116 +++++++++++++++++++------- 3 files changed, 106 insertions(+), 54 deletions(-) diff --git a/internal/acceptance/bake/bake_test.go b/internal/acceptance/bake/bake_test.go index 089af17d..a677bb80 100644 --- a/internal/acceptance/bake/bake_test.go +++ b/internal/acceptance/bake/bake_test.go @@ -407,7 +407,7 @@ var _ = Describe("bake command", func() { command := exec.Command(pathToMain, commandWithArgs...) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) - Eventually(session.Err).Should(gbytes.Say("non-existent-kilnfile.lock: no such file or directory")) + Eventually(session.Err).Should(gbytes.Say("no such file or directory")) }) }) It("errors out when Kilnfile.lock cannot be unmarshalled", func() { diff --git a/internal/commands/bake.go b/internal/commands/bake.go index a4dc469d..dfafab4e 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -1,7 +1,6 @@ package commands import ( - "bytes" "crypto/sha256" "encoding/hex" "errors" @@ -125,6 +124,8 @@ func NewBake(fs billy.Filesystem, releasesService baking.ReleasesService, outLog writeBakeRecord: writeBakeRecord, + loadKilnfile: cargo.ReadKilnfile, + metadata: metadataService, boshVariables: builder.MetadataPartsDirectoryReader{}, @@ -141,6 +142,11 @@ func NewBake(fs billy.Filesystem, releasesService baking.ReleasesService, outLog } } +func (bake Bake) WithKilnfileFunc(fn func(string) (cargo.Kilnfile, error)) Bake { + bake.loadKilnfile = fn + return bake +} + type writeBakeRecordSignature func(string, string, string, []byte) error type Bake struct { @@ -153,6 +159,8 @@ type Bake struct { stemcell stemcellService releases fromDirectories + loadKilnfile func(string) (cargo.Kilnfile, error) + writeBakeRecord writeBakeRecordSignature KilnVersion string @@ -213,6 +221,7 @@ func NewBakeWithInterfaces(interpolator interpolator, tileWriter tileWriter, out icon: iconService, metadata: metadataService, writeBakeRecord: writeBakeRecordFn, + loadKilnfile: cargo.ReadKilnfile, boshVariables: boshVariablesService, forms: formsService, @@ -436,6 +445,10 @@ func (b Bake) Execute(args []string) error { return fmt.Errorf("failed to parse template variables: %s", err) } + if err := BakeArgumentsFromKilnfileConfiguration(&b.Options, templateVariables, b.loadKilnfile); err != nil { + return fmt.Errorf("failed to load bake configuration from Kilnfile: %w", err) + } + releaseManifests, err := b.releases.FromDirectories(b.Options.ReleaseDirectories) if err != nil { return fmt.Errorf("failed to parse releases: %s", err) @@ -447,16 +460,6 @@ func (b Bake) Execute(args []string) error { // TODO remove when stemcell tarball is deprecated stemcellManifest, err = b.stemcell.FromTarball(b.Options.StemcellTarball) } else if b.Options.Kilnfile != "" { - if !b.Options.StubReleases { - if err := BakeArgumentsFromKilnfileConfiguration(&b.Options, templateVariables); err != nil { - return fmt.Errorf("failed to parse releases: %s", err) - } - } - templateVariables, err = b.templateVariables.FromPathsAndPairs(b.Options.VariableFiles, b.Options.Variables) - if err != nil { - return fmt.Errorf("failed to parse template variables: %s", err) - } - stemcellManifests, err = b.stemcell.FromKilnfile(b.Options.Kilnfile) } else if len(b.Options.StemcellsDirectories) > 0 { stemcellManifests, err = b.stemcell.FromDirectories(b.Options.StemcellsDirectories) @@ -598,23 +601,16 @@ func (b Bake) Usage() jhanda.Usage { } } -func BakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[string]any) error { - if options.Kilnfile == "" { +func BakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[string]any, loadKilnfile func(string) (cargo.Kilnfile, error)) error { + if options.Kilnfile == "" || options.StemcellTarball != "" || len(options.StemcellsDirectories) > 0 { return nil } - if variables == nil { - variables = make(map[string]any) - } - buf, err := os.ReadFile(options.Kilnfile) + kf, err := loadKilnfile(options.Kilnfile) if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil - } return err } - kf, err := cargo.InterpolateAndParseKilnfile(bytes.NewReader(buf), variables) - if err != nil { - return err + if len(kf.BakeConfigurations) == 0 { + return nil } if tileName, ok := variables[builder.TileNameVariable]; ok { name, ok := tileName.(string) diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 7b02eff4..75b316a2 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -2,6 +2,7 @@ package commands_test import ( "errors" + "github.com/pivotal-cf/kiln/pkg/cargo" "log" "os" "path/filepath" @@ -80,7 +81,7 @@ var _ = Describe("Bake", func() { fakeChecksummer = &fakes.Checksummer{} fakeIconService = &fakes.IconService{} fakeInterpolator = &fakes.Interpolator{} - fakeBakeRecordFunc = new(fakeWriteBakeRecordFunc) + fakeBakeRecordFunc = &fakeWriteBakeRecordFunc{} fakeLogger = log.New(GinkgoWriter, "", 0) @@ -187,6 +188,7 @@ var _ = Describe("Bake", func() { fakeFetcher = &fakes.Fetch{} fakeFetcher.ExecuteReturns(nil) bake = commands.NewBakeWithInterfaces(fakeInterpolator, fakeTileWriter, fakeLogger, fakeLogger, fakeTemplateVariablesService, fakeBOSHVariablesService, fakeReleasesService, fakeStemcellService, fakeFormsService, fakeInstanceGroupsService, fakeJobsService, fakePropertiesService, fakeRuntimeConfigsService, fakeIconService, fakeMetadataService, fakeChecksummer, fakeFetcher, fakeFilesystem, fakeHomeDirFunc, fakeBakeRecordFunc.call) + bake = bake.WithKilnfileFunc(func(s string) (cargo.Kilnfile, error) { return cargo.Kilnfile{}, nil }) }) AfterEach(func() { @@ -395,6 +397,24 @@ var _ = Describe("Bake", func() { Expect(string(fakeBakeRecordFunc.productTemplate)).To(Equal("some-interpolated-metadata"), "it gives the bake recorder the product template") }) + Context("when bake configuration is in the Kilnfile", func() { + BeforeEach(func() { + bake = bake.WithKilnfileFunc(func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{ + {Metadata: "peach.yml"}, + }, + }, nil + }) + }) + When("a metadata flag is not passed", func() { + It("it uses the value from the bake configuration", func() { + err := bake.Execute([]string{}) + Expect(err).To(Not(HaveOccurred())) + Expect(fakeMetadataService.ReadArgsForCall(0)).To(Equal("peach.yml")) + }) + }) + }) Context("when --stub-releases is specified", func() { It("doesn't fetch releases", func() { err := bake.Execute([]string{ @@ -946,71 +966,107 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { It("handles empty options and variables", func() { opts := &commands.BakeOptions{} variables := map[string]any{} + loadKilnfile := func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{}, nil + } - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) Expect(err).NotTo(HaveOccurred()) }) - It("handles a Kilnfile that does not exist", func() { + It("handles when an error occurs loading the kilnfile", func() { opts := &commands.BakeOptions{ Standard: flags.Standard{ - Kilnfile: "/does/not/exist", + Kilnfile: "non-empty-path/Kilnfile", }, } variables := map[string]any{} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables) - Expect(err).NotTo(HaveOccurred()) + loadKilnfile := func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{}, os.ErrNotExist + } + + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + Expect(err).To(HaveOccurred()) }) - const validKilnfile = `--- -release_sources: - - type: s3 - compiled: true - bucket: bucket - region: region - access_key_id: access_key_id - secret_access_key: secret_access_key - path_template: path_template -` When("passing a valid Kilnfile", func() { var opts *commands.BakeOptions - BeforeEach(func() { - tempDir, err := os.MkdirTemp("", "bake_") - Expect(err).NotTo(HaveOccurred()) - - path := filepath.Join(tempDir, "Kilnfile") - Expect(os.WriteFile(path, []byte(validKilnfile), 0o644)).ToNot(HaveOccurred()) + const kilnfilePath = "tile/Kilnfile" + BeforeEach(func() { opts = &commands.BakeOptions{ Standard: flags.Standard{ - Kilnfile: path, + Kilnfile: kilnfilePath, }, } }) It("handles empty variables", func() { + var kilnfilePathArg string + loadKilnfile := func(s string) (cargo.Kilnfile, error) { + kilnfilePathArg = s + return cargo.Kilnfile{}, nil + } + variables := map[string]any{} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) Expect(err).NotTo(HaveOccurred()) + Expect(kilnfilePathArg).To(Equal(kilnfilePath)) }) - It("handles a valid tile_name variable", func() { - variables := map[string]any{"tile_name": "SRT"} - - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables) - Expect(err).NotTo(HaveOccurred()) + When("there are multiple tile configurations", func() { + var loadKilnfile func(string) (cargo.Kilnfile, error) + BeforeEach(func() { + loadKilnfile = func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{ + { + TileName: "peach", + Metadata: "peach.yml", + }, + { + TileName: "pair", + Metadata: "pair.yml", + }, + }, + }, nil + } + }) + It("handles getting the first configuration by name", func() { + variables := map[string]any{"tile_name": "pair"} + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + Expect(err).NotTo(HaveOccurred()) + Expect(opts.Metadata).To(Equal("pair.yml")) + }) + It("handles getting the second configuration by name", func() { + variables := map[string]any{"tile_name": "peach"} + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + Expect(err).NotTo(HaveOccurred()) + Expect(opts.Metadata).To(Equal("peach.yml")) + }) + //It("handles getting the first configuration when no tile_name is passed", func() { + // variables := map[string]any{} + // err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + // Expect(err).NotTo(HaveOccurred()) + // Expect(opts.Metadata).To(Equal("peach.yml")) + //}) }) It("returns an error for unexpected tile_name type", func() { variables := map[string]any{"tile_name": 8675309} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{{TileName: "apple"}}, + }, nil + }) Expect(err).To(MatchError("tile_name value must be a string got value 8675309 with type int")) }) }) + }) type fakeWriteBakeRecordFunc struct { From f1957dfa0a6a0ae9f90dbb5415c58b874b670474 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 21 Mar 2024 11:53:51 -0700 Subject: [PATCH 3/6] add --tile-name flag to simplfy bake from configuration --- internal/commands/bake.go | 60 +++++++++++++++++++++-------- internal/commands/bake_test.go | 70 ++++++++++++++++++++++++++++++++++ internal/commands/rebake.go | 2 +- 3 files changed, 115 insertions(+), 17 deletions(-) diff --git a/internal/commands/bake.go b/internal/commands/bake.go index dfafab4e..cd3753ca 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -142,6 +142,8 @@ func NewBake(fs billy.Filesystem, releasesService baking.ReleasesService, outLog } } +// WithKilnfileFunc overrides the funcion used to parse the Kilnfile. +// It is for setting up tests. func (bake Bake) WithKilnfileFunc(fn func(string) (cargo.Kilnfile, error)) Bake { bake.loadKilnfile = fn return bake @@ -205,6 +207,8 @@ type BakeOptions struct { Version string `short:"v" long:"version" description:"version of the tile"` SkipFetchReleases bool `short:"sfr" long:"skip-fetch" description:"skips the automatic release fetch for all release directories" alias:"skip-fetch-directories"` + TileName string `short:"t" long:"tile-name" description:"select the bake_configuration matching the tile-name from the Kilnfile"` + IsFinal bool `long:"final" description:"this flag causes build metadata to be written to bake_records"` } @@ -440,15 +444,23 @@ func (b Bake) Execute(args []string) error { b.errLogger.Println("warning: --stemcell-tarball is being deprecated in favor of --stemcells-directory") } - templateVariables, err := b.templateVariables.FromPathsAndPairs(b.Options.VariableFiles, b.Options.Variables) - if err != nil { - return fmt.Errorf("failed to parse template variables: %s", err) + var templateVariables map[string]any + if b.Options.TileName != "" { + if templateVariables == nil { + templateVariables = make(map[string]any) + } + templateVariables[builder.TileNameVariable] = b.Options.TileName } if err := BakeArgumentsFromKilnfileConfiguration(&b.Options, templateVariables, b.loadKilnfile); err != nil { return fmt.Errorf("failed to load bake configuration from Kilnfile: %w", err) } + templateVariables, err = b.templateVariables.FromPathsAndPairs(b.Options.VariableFiles, b.Options.Variables) + if err != nil { + return fmt.Errorf("failed to parse template variables: %s", err) + } + releaseManifests, err := b.releases.FromDirectories(b.Options.ReleaseDirectories) if err != nil { return fmt.Errorf("failed to parse releases: %s", err) @@ -609,29 +621,45 @@ func BakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[ if err != nil { return err } - if len(kf.BakeConfigurations) == 0 { - return nil - } - if tileName, ok := variables[builder.TileNameVariable]; ok { - name, ok := tileName.(string) + + if variableValue, ok := variables[builder.TileNameVariable]; ok { + variableString, ok := variableValue.(string) if !ok { - return fmt.Errorf("%s value must be a string got value %#[2]v with type %[2]T", builder.TileNameVariable, tileName) - } - if index := slices.IndexFunc(kf.BakeConfigurations, func(configuration cargo.BakeConfiguration) bool { - return configuration.TileName == name - }); index >= 0 { - fromConfiguration(options, kf.BakeConfigurations[index]) + return fmt.Errorf("%s value must be a string got value %#[2]v with type %[2]T", builder.TileNameVariable, variableValue) } + options.TileName = variableString + } + + if len(kf.BakeConfigurations) == 0 { + return nil } else if len(kf.BakeConfigurations) == 1 { configuration := kf.BakeConfigurations[0] + if options.TileName != "" && options.TileName != configuration.TileName { + return fmt.Errorf("the provided tile_name %q does not match the configuration %q", options.TileName, configuration.TileName) + } fromConfiguration(options, configuration) - if configuration.TileName != "" { - variables[builder.TileNameVariable] = configuration.TileName + } else if _, ok := variables[builder.TileNameVariable]; ok { + index := slices.IndexFunc(kf.BakeConfigurations, func(configuration cargo.BakeConfiguration) bool { + return configuration.TileName == options.TileName + }) + if index < 0 { + return errorBakeConfigurationNotFound(options, kf) } + fromConfiguration(options, kf.BakeConfigurations[index]) } return nil } +func errorBakeConfigurationNotFound(options *BakeOptions, kf cargo.Kilnfile) error { + names := make([]string, 0, len(kf.BakeConfigurations)) + for _, config := range kf.BakeConfigurations { + names = append(names, config.TileName) + } + slices.Sort(names) + names = slices.Compact(names) + return fmt.Errorf("the provided tile_name %q does not match any configuration. The available names are: %v", options.TileName, names) +} + func fromConfiguration(b *BakeOptions, configuration cargo.BakeConfiguration) { if len(configuration.Metadata) > 0 { b.Metadata = configuration.Metadata diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 75b316a2..9a14d5e4 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -415,6 +415,37 @@ var _ = Describe("Bake", func() { }) }) }) + Context("when bake configuration has multiple options", func() { + BeforeEach(func() { + bake = bake.WithKilnfileFunc(func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{ + { + TileName: "p-each", + Metadata: "peach.yml", + }, + { + TileName: "p-air", + Metadata: "pair.yml", + }, + { + TileName: "p-lum", + Metadata: "plum.yml", + }, + }, + }, nil + }) + }) + When("a the tile flag is passed", func() { + It("it uses the value from the bake configuration with the correct name", func() { + err := bake.Execute([]string{ + "--tile-name=p-each", + }) + Expect(err).To(Not(HaveOccurred())) + Expect(fakeMetadataService.ReadArgsForCall(0)).To(Equal("peach.yml")) + }) + }) + }) Context("when --stub-releases is specified", func() { It("doesn't fetch releases", func() { err := bake.Execute([]string{ @@ -1017,6 +1048,34 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { Expect(kilnfilePathArg).To(Equal(kilnfilePath)) }) + When("there is one tile configuration", func() { + var loadKilnfile func(string) (cargo.Kilnfile, error) + BeforeEach(func() { + loadKilnfile = func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{ + {TileName: "peach", Metadata: "peach.yml"}, + }, + }, nil + } + }) + When("tile_name is unset", func() { + It("handles getting the first configuration", func() { + variables := make(map[string]any) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + Expect(err).NotTo(HaveOccurred()) + Expect(opts.Metadata).To(Equal("peach.yml")) + }) + }) + When("a tile_name is a variable and does not match the bake configuration", func() { + It("handles getting the first configuration", func() { + variables := map[string]any{builder.TileNameVariable: "banana"} + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + Expect(err).To(HaveOccurred()) + }) + }) + }) + When("there are multiple tile configurations", func() { var loadKilnfile func(string) (cargo.Kilnfile, error) BeforeEach(func() { @@ -1065,6 +1124,17 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { }) Expect(err).To(MatchError("tile_name value must be a string got value 8675309 with type int")) }) + + It("returns an error for unexpected tile_name type", func() { + variables := map[string]any{"tile_name": 8675309} + + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, func(s string) (cargo.Kilnfile, error) { + return cargo.Kilnfile{ + BakeConfigurations: []cargo.BakeConfiguration{{TileName: "apple"}}, + }, nil + }) + Expect(err).To(MatchError("tile_name value must be a string got value 8675309 with type int")) + }) }) }) diff --git a/internal/commands/rebake.go b/internal/commands/rebake.go index e5aa29ad..3729618f 100644 --- a/internal/commands/rebake.go +++ b/internal/commands/rebake.go @@ -74,7 +74,7 @@ func (cmd ReBake) Execute(args []string) error { } if !record.IsEquivalent(newRecord, log.New(os.Stderr, "bake record diff: ", 0)) { - return fmt.Errorf("expected tile bake records to be equivilant") + return fmt.Errorf("expected tile bake records to be equivalent") } return nil From 8211c780b2ab9f7567935f60060ceb2b075f8a9e Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 21 Mar 2024 14:20:25 -0700 Subject: [PATCH 4/6] load tile_name variable from bake configuration Co-authored-by: Ramkumar Vengadakrishnan --- .../workflows/baking_a_tile.feature | 7 ++ .../tiles/multiple-tile-names/Kilnfile | 19 ++++++ .../tiles/multiple-tile-names/Kilnfile.lock | 3 + .../configuration/networking.yml | 5 ++ .../tiles/multiple-tile-names/gopher.png | Bin 0 -> 62860 bytes .../multiple-tile-names/job_types/hello.yml | 55 +++++++++++++++ .../multiple-tile-names/product_template.yml | 29 ++++++++ .../multiple-tile-names/variables/goodbye.yml | 2 + .../multiple-tile-names/variables/hello.yml | 2 + .../tiles/multiple-tile-names/version | 1 + internal/commands/bake.go | 32 ++++----- internal/commands/bake_test.go | 63 ++++++------------ 12 files changed, 156 insertions(+), 62 deletions(-) create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile.lock create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/configuration/networking.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/gopher.png create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/job_types/hello.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/product_template.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/variables/goodbye.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/variables/hello.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/multiple-tile-names/version diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index e346af95..b88b55b3 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -26,6 +26,13 @@ Feature: As a developer, I want to bake a tile | --stub-releases | Then a Tile is created + Scenario: it handles tiles with multiple tile names + Given I have a tile source directory "testdata/tiles/multiple-tile-names" + When I invoke kiln + | bake | + | --tile-name=goodbye | + Then a Tile is created + Scenario: it bakes a tile from a bake record Given I have a tile source directory "testdata/tiles/bake-record" When I invoke kiln diff --git a/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile new file mode 100644 index 00000000..9cef3095 --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile @@ -0,0 +1,19 @@ +bake_configurations: + - tile_name: hello + metadata_filepath: product_template.yml + variables_filepaths: + - variables/hello.yml + icon_filepath: "gopher.png" + instance_groups_directories: + - job_types + properties_directories: + - configuration + - tile_name: goodbye + metadata_filepath: product_template.yml + variables_filepaths: + - variables/goodbye.yml + icon_filepath: "gopher.png" + instance_groups_directories: + - job_types + properties_directories: + - configuration \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile.lock b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile.lock new file mode 100644 index 00000000..c015566a --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/Kilnfile.lock @@ -0,0 +1,3 @@ +stemcell_criteria: + os: ubuntu-jammy + version: "1.329" \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/configuration/networking.yml b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/configuration/networking.yml new file mode 100644 index 00000000..eccbf65a --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/configuration/networking.yml @@ -0,0 +1,5 @@ +--- +- name: port + type: port + configurable: true + default: 8080 \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/gopher.png b/internal/acceptance/workflows/testdata/tiles/multiple-tile-names/gopher.png new file mode 100644 index 0000000000000000000000000000000000000000..3d878e52682b6a7d47499f1d3414c69cec00ea45 GIT binary patch literal 62860 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 0 { return nil } @@ -622,14 +621,6 @@ func BakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[ return err } - if variableValue, ok := variables[builder.TileNameVariable]; ok { - variableString, ok := variableValue.(string) - if !ok { - return fmt.Errorf("%s value must be a string got value %#[2]v with type %[2]T", builder.TileNameVariable, variableValue) - } - options.TileName = variableString - } - if len(kf.BakeConfigurations) == 0 { return nil } else if len(kf.BakeConfigurations) == 1 { @@ -638,7 +629,8 @@ func BakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[ return fmt.Errorf("the provided tile_name %q does not match the configuration %q", options.TileName, configuration.TileName) } fromConfiguration(options, configuration) - } else if _, ok := variables[builder.TileNameVariable]; ok { + options.TileName = configuration.TileName + } else { index := slices.IndexFunc(kf.BakeConfigurations, func(configuration cargo.BakeConfiguration) bool { return configuration.TileName == options.TileName }) diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 9a14d5e4..65d4b3a0 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -402,7 +402,7 @@ var _ = Describe("Bake", func() { bake = bake.WithKilnfileFunc(func(s string) (cargo.Kilnfile, error) { return cargo.Kilnfile{ BakeConfigurations: []cargo.BakeConfiguration{ - {Metadata: "peach.yml"}, + {TileName: "p-each", Metadata: "peach.yml"}, }, }, nil }) @@ -414,6 +414,14 @@ var _ = Describe("Bake", func() { Expect(fakeMetadataService.ReadArgsForCall(0)).To(Equal("peach.yml")) }) }) + When("generating metadata", func() { + It("it uses the value from the bake configuration", func() { + err := bake.Execute([]string{}) + Expect(err).To(Not(HaveOccurred())) + input, _, _ := fakeInterpolator.InterpolateArgsForCall(0) + Expect(input.Variables).To(HaveKeyWithValue("tile_name", "p-each")) + }) + }) }) Context("when bake configuration has multiple options", func() { BeforeEach(func() { @@ -995,13 +1003,10 @@ var _ = Describe("Bake", func() { var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { It("handles empty options and variables", func() { - opts := &commands.BakeOptions{} - variables := map[string]any{} loadKilnfile := func(s string) (cargo.Kilnfile, error) { return cargo.Kilnfile{}, nil } - - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + err := commands.BakeArgumentsFromKilnfileConfiguration(new(commands.BakeOptions), loadKilnfile) Expect(err).NotTo(HaveOccurred()) }) @@ -1011,13 +1016,12 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { Kilnfile: "non-empty-path/Kilnfile", }, } - variables := map[string]any{} loadKilnfile := func(s string) (cargo.Kilnfile, error) { return cargo.Kilnfile{}, os.ErrNotExist } - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).To(HaveOccurred()) }) @@ -1034,16 +1038,14 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { } }) - It("handles empty variables", func() { + It("handles empty TileName", func() { var kilnfilePathArg string loadKilnfile := func(s string) (cargo.Kilnfile, error) { kilnfilePathArg = s return cargo.Kilnfile{}, nil } - - variables := map[string]any{} - - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + opts.TileName = "" + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).NotTo(HaveOccurred()) Expect(kilnfilePathArg).To(Equal(kilnfilePath)) }) @@ -1061,16 +1063,15 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { }) When("tile_name is unset", func() { It("handles getting the first configuration", func() { - variables := make(map[string]any) - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).NotTo(HaveOccurred()) Expect(opts.Metadata).To(Equal("peach.yml")) }) }) When("a tile_name is a variable and does not match the bake configuration", func() { It("handles getting the first configuration", func() { - variables := map[string]any{builder.TileNameVariable: "banana"} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + opts.TileName = "banana" + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).To(HaveOccurred()) }) }) @@ -1095,14 +1096,14 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { } }) It("handles getting the first configuration by name", func() { - variables := map[string]any{"tile_name": "pair"} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + opts.TileName = "pair" + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).NotTo(HaveOccurred()) Expect(opts.Metadata).To(Equal("pair.yml")) }) It("handles getting the second configuration by name", func() { - variables := map[string]any{"tile_name": "peach"} - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, loadKilnfile) + opts.TileName = "peach" + err := commands.BakeArgumentsFromKilnfileConfiguration(opts, loadKilnfile) Expect(err).NotTo(HaveOccurred()) Expect(opts.Metadata).To(Equal("peach.yml")) }) @@ -1113,28 +1114,6 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { // Expect(opts.Metadata).To(Equal("peach.yml")) //}) }) - - It("returns an error for unexpected tile_name type", func() { - variables := map[string]any{"tile_name": 8675309} - - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, func(s string) (cargo.Kilnfile, error) { - return cargo.Kilnfile{ - BakeConfigurations: []cargo.BakeConfiguration{{TileName: "apple"}}, - }, nil - }) - Expect(err).To(MatchError("tile_name value must be a string got value 8675309 with type int")) - }) - - It("returns an error for unexpected tile_name type", func() { - variables := map[string]any{"tile_name": 8675309} - - err := commands.BakeArgumentsFromKilnfileConfiguration(opts, variables, func(s string) (cargo.Kilnfile, error) { - return cargo.Kilnfile{ - BakeConfigurations: []cargo.BakeConfiguration{{TileName: "apple"}}, - }, nil - }) - Expect(err).To(MatchError("tile_name value must be a string got value 8675309 with type int")) - }) }) }) From fd2837feed326f05c8992062a860729bd14fbe05 Mon Sep 17 00:00:00 2001 From: Ramkumar Vengadakrishnan Date: Fri, 22 Mar 2024 16:35:11 -0500 Subject: [PATCH 5/6] docs: Updated readme with re-bake and bake --final info - Also fixed --help argument for re-bake command Authored-by: Ramkumar Vengadakrishnan --- README.md | 17 +++++++++++++++++ internal/commands/rebake.go | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2070c2e0..984215e9 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ Commands: glaze Pin versions in Kilnfile to match lock. help prints this usage information publish publish tile on Pivnet + re-bake re-bake constructs a tile from a bake record release-notes generates release notes from bosh-release release notes sync-with-local update the Kilnfile.lock based on local releases test Test manifest for a product @@ -224,6 +225,13 @@ tile. There are very few reasons a tile developer should want to do this, but if you do, you can include these extra files here. The flag can be specified multiple times to embed multiple files or directories. +##### `--final` + +The `--final` flag is to bake a final release tile. When passing the --final flag, +Kiln creates a baked record file with metadata like source revision SHA, tile version, kiln version and +file checksums. This bake record file will be created under bake_records folder. This +bake record file can later be used to re-bake the tile. + ##### `--forms-directory` The `--forms-directory` flag takes a path to a directory that contains one @@ -519,7 +527,16 @@ provides_product_versions: ``` +### `re-bake` +It constructs a tile from a given bake record file. + +To run the command, you simply need to be within a tile directory and execute the following command: +``` +$ kiln re-bake --output-file tile.pivotal bake_records/1.0.0.json +``` +Any variables that Kilnfile needs for the kiln re-bake command should be set in +~/.kiln/credentials.yml file ### `test` diff --git a/internal/commands/rebake.go b/internal/commands/rebake.go index 3729618f..78113a4b 100644 --- a/internal/commands/rebake.go +++ b/internal/commands/rebake.go @@ -84,6 +84,6 @@ func (cmd ReBake) Usage() jhanda.Usage { return jhanda.Usage{ Description: "re-bake (aka record bake) builds a tile from a bake record. You must check out the repository to the revision of the source_revision in the bake record before running this command.", ShortDescription: "re-bake constructs a tile from a bake record", - Flags: &cmd.Options, + Flags: cmd.Options, } } From ec0970bb3c1baaa85a772a59c650e1e0fe12fb5e Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Tue, 26 Mar 2024 12:09:15 -0700 Subject: [PATCH 6/6] run gofumpt --- internal/commands/bake_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 65d4b3a0..45e1b3b7 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -2,7 +2,6 @@ package commands_test import ( "errors" - "github.com/pivotal-cf/kiln/pkg/cargo" "log" "os" "path/filepath" @@ -11,6 +10,8 @@ import ( "testing" "time" + "github.com/pivotal-cf/kiln/pkg/cargo" + "github.com/pivotal-cf/kiln/pkg/bake" . "github.com/onsi/ginkgo" @@ -1115,7 +1116,6 @@ var _ = Describe("BakeArgumentsFromKilnfileConfiguration", func() { //}) }) }) - }) type fakeWriteBakeRecordFunc struct {