Skip to content

Commit

Permalink
support multiple config versions in single file.
Browse files Browse the repository at this point in the history
  • Loading branch information
gsquared94 committed Jan 8, 2021
1 parent 07bf86c commit 6f5d2cd
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 38 deletions.
36 changes: 12 additions & 24 deletions pkg/skaffold/schema/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ func ParseConfig(filename string) ([]util.VersionedConfig, error) {
if err != nil {
return nil, fmt.Errorf("read skaffold config: %w", err)
}
factory, err := configFactoryFromAPIVersion(buf)
factories, err := configFactoryFromAPIVersion(buf)
if err != nil {
return nil, err
}
buf, err = removeYamlAnchors(buf)
if err != nil {
return nil, fmt.Errorf("unable to re-marshal YAML without dotted keys: %w", err)
}
return parseConfig(buf, factory)
return parseConfig(buf, factories)
}

// ParseConfigAndUpgrade reads a configuration file and upgrades it to a given version.
Expand Down Expand Up @@ -211,14 +211,14 @@ func ParseConfigAndUpgrade(filename, toVersion string) ([]util.VersionedConfig,
}

// configFactoryFromAPIVersion checks that all configs in the input stream have the same API version, and returns a function to create a config with that API version.
func configFactoryFromAPIVersion(buf []byte) (func() util.VersionedConfig, error) {
func configFactoryFromAPIVersion(buf []byte) ([]func() util.VersionedConfig, error) {
// This is to quickly check that it's possibly a skaffold.yaml,
// without parsing the whole file.
if !bytes.Contains(buf, []byte("apiVersion")) {
return nil, errors.New("missing apiVersion")
}

var cfgVersions []APIVersion
var factories []func() util.VersionedConfig
b := bytes.NewReader(buf)
decoder := yaml.NewDecoder(b)
for {
Expand All @@ -230,25 +230,13 @@ func configFactoryFromAPIVersion(buf []byte) (func() util.VersionedConfig, error
if err != nil {
return nil, fmt.Errorf("parsing api version: %w", err)
}
cfgVersions = append(cfgVersions, v)
}

if len(cfgVersions) == 0 {
return nil, errors.New("invalid config")
}
apiVersion := cfgVersions[0].Version

for _, v := range cfgVersions {
if v.Version != apiVersion {
return nil, errors.New("multiple config versions detected in the same file")
factory, present := SchemaVersions.Find(v.Version)
if !present {
return nil, fmt.Errorf("unknown api version: %q", v.Version)
}
factories = append(factories, factory)
}

factory, present := SchemaVersions.Find(apiVersion)
if !present {
return nil, fmt.Errorf("unknown api version: %q", apiVersion)
}
return factory, nil
return factories, nil
}

// removeYamlAnchors removes all top-level keys starting with `.` from the input stream so they can be used as YAML anchors
Expand Down Expand Up @@ -285,13 +273,13 @@ func removeYamlAnchors(buf []byte) ([]byte, error) {
return out.Bytes(), nil
}

func parseConfig(buf []byte, factory func() util.VersionedConfig) ([]util.VersionedConfig, error) {
func parseConfig(buf []byte, factories []func() util.VersionedConfig) ([]util.VersionedConfig, error) {
b := bytes.NewReader(buf)
decoder := yaml.NewDecoder(b)
decoder.KnownFields(true)
var cfgs []util.VersionedConfig
for {
cfg := factory()
for index := 0; index < len(factories); index++ {
cfg := factories[index]()
err := decoder.Decode(cfg)
if err == io.EOF {
break
Expand Down
44 changes: 30 additions & 14 deletions pkg/skaffold/schema/versions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func TestParseConfigAndUpgrade(t *testing.T) {
withGoogleCloudBuild("ID",
withShaTagger(),
withDockerArtifact("image1", "./examples/app1", "Dockerfile.dev"),
withBazelArtifact("image2", "./examples/app2", "//:example.tar"),
withBazelArtifact(),
),
withKubectlDeploy("dep.yaml", "svc.yaml"),
withLogsPrefix("container"),
Expand All @@ -268,21 +268,43 @@ func TestParseConfigAndUpgrade(t *testing.T) {
withGoogleCloudBuild("ID",
withShaTagger(),
withDockerArtifact("image1", "./examples/app1", "Dockerfile.dev"),
withBazelArtifact("image2", "./examples/app2", "//:example.tar"),
withBazelArtifact(),
),
withKubectlDeploy("dep.yaml", "svc.yaml"),
withLogsPrefix("container"),
)},
},
{
apiVersion: []string{latest.Version, latest.Version},
description: "Multiple complete config",
description: "Multiple complete config with same API versions",
config: []string{completeConfig, completeClusterConfig},
expected: []util.VersionedConfig{config(
withGoogleCloudBuild("ID",
withShaTagger(),
withDockerArtifact("image1", "./examples/app1", "Dockerfile.dev"),
withBazelArtifact("image2", "./examples/app2", "//:example.tar"),
withBazelArtifact(),
),
withKubectlDeploy("dep.yaml", "svc.yaml"),
withLogsPrefix("container"),
), config(
withClusterBuild("secret-name", "/secret", "nskaniko", "/secret.json", "120m",
withGitTagger(),
withDockerConfig("config-name", "/kaniko/.docker"),
withKanikoArtifact(),
),
withKubectlDeploy("k8s/*.yaml"),
withLogsPrefix("container"),
)},
},
{
apiVersion: []string{latest.Version, v2beta8.Version},
description: "Multiple complete config with different API versions",
config: []string{completeConfig, completeClusterConfig},
expected: []util.VersionedConfig{config(
withGoogleCloudBuild("ID",
withShaTagger(),
withDockerArtifact("image1", "./examples/app1", "Dockerfile.dev"),
withBazelArtifact(),
),
withKubectlDeploy("dep.yaml", "svc.yaml"),
withLogsPrefix("container"),
Expand Down Expand Up @@ -341,12 +363,6 @@ func TestParseConfigAndUpgrade(t *testing.T) {
config: []string{minimalConfig},
shouldErr: true,
},
{
apiVersion: []string{latest.Version, v2beta8.Version},
description: "ApiVersion mismatch",
config: []string{simpleConfig, simpleConfig},
shouldErr: true,
},
{
apiVersion: []string{latest.Version},
description: "invalid statusCheckDeadline",
Expand Down Expand Up @@ -551,14 +567,14 @@ func withDockerArtifact(image, workspace, dockerfile string) func(*latest.BuildC
}
}

func withBazelArtifact(image, workspace, target string) func(*latest.BuildConfig) {
func withBazelArtifact() func(*latest.BuildConfig) {
return func(cfg *latest.BuildConfig) {
cfg.Artifacts = append(cfg.Artifacts, &latest.Artifact{
ImageName: image,
Workspace: workspace,
ImageName: "image2",
Workspace: "./examples/app2",
ArtifactType: latest.ArtifactType{
BazelArtifact: &latest.BazelArtifact{
BuildTarget: target,
BuildTarget: "//:example.tar",
},
},
})