Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

load kiln arguments from Kilnfile #473

Merged
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
3 changes: 3 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ function main() {
cwd="${1}"

pushd "${cwd}" > /dev/null
export CGO_ENABLED
CGO_ENABLED=0

go install \
-ldflags "-X main.version=$(git rev-parse HEAD)" \
-gcflags=-trimpath="${cwd}" \
Expand Down
7 changes: 7 additions & 0 deletions internal/acceptance/workflows/baking_a_tile.feature
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ Feature: As a developer, I want to bake a tile
And "bake_records/0.2.0-dev.json" contains substring: "version": "0.2.0-dev"
And "bake_records/0.2.0-dev.json" contains substring: "source_revision":
And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests"

Scenario: it reads directory configuration from Kilnfile
Given I have a tile source directory "testdata/tiles/non-standard-paths"
When I invoke kiln
| bake |
| --stub-releases |
Then a Tile is created
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bake_configurations:
- tile_name: hello
metadata_filepath: product_template.yml
variables_filepaths:
- text.yml
icon_filepath: "gopher.png"
instance_groups_directories:
- job_types
properties_directories:
- configuration
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
stemcell_criteria:
os: ubuntu-jammy
version: "1.329"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: port
type: port
configurable: true
default: 8080
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: hello-server
label: Server
resource_label: Server
description: HTTP Server

templates: []

static_ip: 1
dynamic_ip: 0

max_in_flight: 1
single_az_only: true

instance_definition:
name: instances
type: integer
label: Instances
configurable: true
default: 1
constraints:
min: 0
max: 1

resource_definitions:
- name: ram
type: integer
label: RAM
configurable: true
default: 1024
constraints:
min: 1024

- name: ephemeral_disk
type: integer
label: Ephemeral Disk
configurable: true
default: 4000
constraints:
min: 2000

- name: persistent_disk
type: integer
label: Persistent Disk
configurable: false
default: 4000
constraints:
min: 2000

- name: cpu
type: integer
label: CPU
configurable: true
default: 1
constraints:
min: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: hello
label: "some label"
description: "some description"
icon_image: $( icon )

metadata_version: "2.7.0"
minimum_version_for_upgrade: 0.1.0
product_version: $( version )
provides_product_versions:
- name: hello
version: $( version )

rank: 90
serial: false

releases: []

stemcell_criteria: $( stemcell )

job_types:
- $( instance_group "hello-server" )

runtime_configs: []

property_blueprints:
- $( property "port" )

form_types: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
label: "some label"
description: "some description"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.2.3
194 changes: 144 additions & 50 deletions internal/commands/bake.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package commands

import (
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"slices"
"strings"

"github.com/go-git/go-billy/v5"
Expand All @@ -17,6 +19,7 @@ import (
"github.com/pivotal-cf/kiln/internal/builder"
"github.com/pivotal-cf/kiln/internal/commands/flags"
"github.com/pivotal-cf/kiln/internal/helper"
"github.com/pivotal-cf/kiln/pkg/cargo"
"github.com/pivotal-cf/kiln/pkg/source"
)

Expand Down Expand Up @@ -164,32 +167,34 @@ type Bake struct {
metadata metadataService

fetcher jhanda.Command
Options struct {
flags.Standard
flags.FetchBakeOptions

IsFinal bool `long:"final" description:"this flag causes build metadata to be written to bake_records"`

Metadata string `short:"m" long:"metadata" default:"base.yml" description:"path to the metadata file"`
ReleaseDirectories []string `short:"rd" long:"releases-directory" default:"releases" description:"path to a directory containing release tarballs"`
FormDirectories []string `short:"f" long:"forms-directory" default:"forms" description:"path to a directory containing forms"`
IconPath string `short:"i" long:"icon" default:"icon.png" description:"path to icon file"`
InstanceGroupDirectories []string `short:"ig" long:"instance-groups-directory" default:"instance_groups" description:"path to a directory containing instance groups"`
JobDirectories []string `short:"j" long:"jobs-directory" default:"jobs" description:"path to a directory containing jobs"`
MigrationDirectories []string `short:"md" long:"migrations-directory" default:"migrations" description:"path to a directory containing migrations"`
PropertyDirectories []string `short:"pd" long:"properties-directory" default:"properties" description:"path to a directory containing property blueprints"`
RuntimeConfigDirectories []string `short:"rcd" long:"runtime-configs-directory" default:"runtime_configs" description:"path to a directory containing runtime configs"`
BOSHVariableDirectories []string `short:"vd" long:"bosh-variables-directory" default:"bosh_variables" description:"path to a directory containing BOSH variables"`
StemcellTarball string `short:"st" long:"stemcell-tarball" description:"deprecated -- path to a stemcell tarball (NOTE: mutually exclusive with --kilnfile)"`
StemcellsDirectories []string `short:"sd" long:"stemcells-directory" description:"path to a directory containing stemcells (NOTE: mutually exclusive with --kilnfile or --stemcell-tarball)"`
EmbedPaths []string `short:"e" long:"embed" description:"path to files to include in the tile /embed directory"`
OutputFile string `short:"o" long:"output-file" description:"path to where the tile will be output"`
MetadataOnly bool `short:"mo" long:"metadata-only" description:"don't build a tile, output the metadata to stdout"`
Sha256 bool ` long:"sha256" description:"calculates a SHA256 checksum of the output file"`
StubReleases bool `short:"sr" long:"stub-releases" description:"skips importing release tarballs into the tile"`
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"`
}
Options BakeOptions
}

type BakeOptions struct {
flags.Standard
flags.FetchBakeOptions

Metadata string `short:"m" long:"metadata" default:"base.yml" description:"path to the metadata file"`
ReleaseDirectories []string `short:"rd" long:"releases-directory" default:"releases" description:"path to a directory containing release tarballs"`
FormDirectories []string `short:"f" long:"forms-directory" default:"forms" description:"path to a directory containing forms"`
IconPath string `short:"i" long:"icon" default:"icon.png" description:"path to icon file"`
InstanceGroupDirectories []string `short:"ig" long:"instance-groups-directory" default:"instance_groups" description:"path to a directory containing instance groups"`
JobDirectories []string `short:"j" long:"jobs-directory" default:"jobs" description:"path to a directory containing jobs"`
MigrationDirectories []string `short:"md" long:"migrations-directory" default:"migrations" description:"path to a directory containing migrations"`
PropertyDirectories []string `short:"pd" long:"properties-directory" default:"properties" description:"path to a directory containing property blueprints"`
RuntimeConfigDirectories []string `short:"rcd" long:"runtime-configs-directory" default:"runtime_configs" description:"path to a directory containing runtime configs"`
BOSHVariableDirectories []string `short:"vd" long:"bosh-variables-directory" default:"bosh_variables" description:"path to a directory containing BOSH variables"`
StemcellTarball string `short:"st" long:"stemcell-tarball" description:"deprecated -- path to a stemcell tarball (NOTE: mutually exclusive with --kilnfile)"`
StemcellsDirectories []string `short:"sd" long:"stemcells-directory" description:"path to a directory containing stemcells (NOTE: mutually exclusive with --kilnfile or --stemcell-tarball)"`
EmbedPaths []string `short:"e" long:"embed" description:"path to files to include in the tile /embed directory"`
OutputFile string `short:"o" long:"output-file" description:"path to where the tile will be output"`
MetadataOnly bool `short:"mo" long:"metadata-only" description:"don't build a tile, output the metadata to stdout"`
Sha256 bool ` long:"sha256" description:"calculates a SHA256 checksum of the output file"`
StubReleases bool `short:"sr" long:"stub-releases" description:"skips importing release tarballs into the tile"`
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"`

IsFinal bool `long:"final" description:"this flag causes build metadata to be written to bake_records"`
}

func NewBakeWithInterfaces(interpolator interpolator, tileWriter tileWriter, outLogger *log.Logger, errLogger *log.Logger, templateVariablesService templateVariablesService, boshVariablesService metadataTemplatesParser, releasesService fromDirectories, stemcellService stemcellService, formsService metadataTemplatesParser, instanceGroupsService metadataTemplatesParser, jobsService metadataTemplatesParser, propertiesService metadataTemplatesParser, runtimeConfigsService metadataTemplatesParser, iconService iconService, metadataService metadataService, checksummer checksummer, fetcher jhanda.Command, fs FileSystem, homeDir flags.HomeDirFunc, writeBakeRecordFn writeBakeRecordSignature) Bake {
Expand Down Expand Up @@ -392,30 +397,6 @@ func (b Bake) Execute(args []string) error {
}
}

if b.Options.Metadata == "" {
return errors.New("missing required flag \"--metadata\"")
}

if len(b.Options.InstanceGroupDirectories) == 0 && len(b.Options.JobDirectories) > 0 {
return errors.New("--jobs-directory flag requires --instance-groups-directory to also be specified")
}

if b.Options.Kilnfile != "" && b.Options.StemcellTarball != "" {
return errors.New("--kilnfile cannot be provided when using --stemcell-tarball")
}

if b.Options.Kilnfile != "" && len(b.Options.StemcellsDirectories) > 0 {
return errors.New("--kilnfile cannot be provided when using --stemcells-directory")
}

if b.Options.StemcellTarball != "" && len(b.Options.StemcellsDirectories) > 0 {
return errors.New("--stemcell-tarball cannot be provided when using --stemcells-directory")
}

if b.Options.OutputFile != "" && b.Options.MetadataOnly {
return errors.New("--output-file cannot be provided when using --metadata-only")
}

// TODO: Remove check after deprecation of --stemcell-tarball
if b.Options.StemcellTarball != "" {
b.errLogger.Println("warning: --stemcell-tarball is being deprecated in favor of --stemcells-directory")
Expand All @@ -437,6 +418,14 @@ 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)
}
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)
Expand All @@ -445,6 +434,30 @@ func (b Bake) Execute(args []string) error {
return fmt.Errorf("failed to parse stemcell: %s", err)
}

if b.Options.Metadata == "" {
return errors.New("missing required flag \"--metadata\"")
}

if len(b.Options.InstanceGroupDirectories) == 0 && len(b.Options.JobDirectories) > 0 {
return errors.New("--jobs-directory flag requires --instance-groups-directory to also be specified")
}

if b.Options.Kilnfile != "" && b.Options.StemcellTarball != "" {
return errors.New("--kilnfile cannot be provided when using --stemcell-tarball")
}

if b.Options.Kilnfile != "" && len(b.Options.StemcellsDirectories) > 0 {
return errors.New("--kilnfile cannot be provided when using --stemcells-directory")
}

if b.Options.StemcellTarball != "" && len(b.Options.StemcellsDirectories) > 0 {
return errors.New("--stemcell-tarball cannot be provided when using --stemcells-directory")
}

if b.Options.OutputFile != "" && b.Options.MetadataOnly {
return errors.New("--output-file cannot be provided when using --metadata-only")
}

boshVariables, err := b.boshVariables.ParseMetadataTemplates(b.Options.BOSHVariableDirectories, templateVariables)
if err != nil {
return fmt.Errorf("failed to parse bosh variables: %s", err)
Expand Down Expand Up @@ -551,3 +564,84 @@ func (b Bake) Usage() jhanda.Usage {
Flags: b.Options,
}
}

func bakeArgumentsFromKilnfileConfiguration(options *BakeOptions, variables map[string]any) error {
if options.Kilnfile == "" {
return nil
}
if variables == nil {
variables = make(map[string]any)
}
buf, err := os.ReadFile(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 tileName, ok := variables[builder.TileNameVariable]; ok {
name, ok := tileName.(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])
}
} else if len(kf.BakeConfigurations) == 1 {
configuration := kf.BakeConfigurations[0]
fromConfiguration(options, configuration)
if configuration.TileName != "" {
variables[builder.TileNameVariable] = configuration.TileName
}
}
return nil
}

func fromConfiguration(b *BakeOptions, configuration cargo.BakeConfiguration) {
if len(configuration.Metadata) > 0 {
b.Metadata = configuration.Metadata
}
if len(configuration.FormDirectories) > 0 {
b.FormDirectories = configuration.FormDirectories
}
if len(configuration.IconPath) > 0 {
b.IconPath = configuration.IconPath
}
if len(configuration.InstanceGroupDirectories) > 0 {
b.InstanceGroupDirectories = configuration.InstanceGroupDirectories
}
if len(configuration.JobDirectories) > 0 {
b.JobDirectories = configuration.JobDirectories
}
if len(configuration.MigrationDirectories) > 0 {
b.MigrationDirectories = configuration.MigrationDirectories
}
if len(configuration.PropertyDirectories) > 0 {
b.PropertyDirectories = configuration.PropertyDirectories
}
if len(configuration.RuntimeConfigDirectories) > 0 {
b.RuntimeConfigDirectories = configuration.RuntimeConfigDirectories
}
if len(configuration.BOSHVariableDirectories) > 0 {
b.BOSHVariableDirectories = configuration.BOSHVariableDirectories
}
if len(configuration.EmbedPaths) > 0 {
b.EmbedPaths = configuration.EmbedPaths
}
if len(configuration.VariableFiles) > 0 {
// simplify when go1.22 comes out https://pkg.go.dev/slices@master#Concat
variableFiles := make([]string, 0, len(configuration.VariableFiles)+len(b.VariableFiles))
variableFiles = append(variableFiles, configuration.VariableFiles...)
variableFiles = append(variableFiles, b.VariableFiles...)

slices.Sort(variableFiles)
variableFiles = slices.Compact(variableFiles)
b.VariableFiles = variableFiles
}
}
3 changes: 3 additions & 0 deletions internal/test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ func TestDockerIntegration(t *testing.T) {

func checkDaemonVersion(t *testing.T) {
t.Helper()
if testing.Short() {
t.Skip()
}

constraints, err := semver.NewConstraint(test.MinimumDockerServerVersion)
require.NoError(t, err)
Expand Down
Loading
Loading