Skip to content

Commit

Permalink
add skaffold inspect build-env add googleCloudBuild command (#5824)
Browse files Browse the repository at this point in the history
* add `skaffold inspect build-env add googleCloudBuild` command

* fix function desc

* improve tests
  • Loading branch information
gsquared94 authored May 13, 2021
1 parent ffb7d2b commit d9b397e
Show file tree
Hide file tree
Showing 24 changed files with 1,071 additions and 251 deletions.
75 changes: 73 additions & 2 deletions cmd/skaffold/app/cmd/inspect_build_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ import (
"github.com/spf13/pflag"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect"
buildEnv "github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect/buildEnv"
)

var buildEnvFlags = struct {
profile string
projectID string
diskSizeGb int64
machineType string
timeout string
concurrency int
}{}

func cmdBuildEnv() *cobra.Command {
return NewCmd("build-env").
WithDescription("Interact with skaffold build environment definitions.").
WithPersistentFlagAdder(cmdBuildEnvFlags).
WithCommands(cmdBuildEnvList())
WithCommands(cmdBuildEnvList(), cmdBuildEnvAdd())
}

func cmdBuildEnvList() *cobra.Command {
Expand All @@ -41,8 +51,43 @@ func cmdBuildEnvList() *cobra.Command {
NoArgs(listBuildEnv)
}

func cmdBuildEnvAdd() *cobra.Command {
return NewCmd("add").
WithDescription("Add a new build environment to the default pipeline or to a new or existing profile.").
WithPersistentFlagAdder(cmdBuildEnvAddFlags).
WithCommands(cmdBuildEnvAddGcb())
}

func cmdBuildEnvAddGcb() *cobra.Command {
return NewCmd("googleCloudBuild").
WithDescription("Add a new GoogleCloudBuild build environment definition").
WithLongDescription(`Add a new GoogleCloudBuild build environment definition.
Without the '--profile' flag the new environment definition is added to the default pipeline. With the '--profile' flag it will create a new profile with this build env definition.
In these respective scenarios, it will fail if the build env definition for the default pipeline or the named profile already exists. To override an existing definition use 'skaffold inspect build-env modify' command instead.
Use the '--module' filter to specify the individual module to target. Otherwise, it'll be applied to all modules defined in the target file. Also, with the '--profile' flag if the target config imports other configs as dependencies, then the new profile will be recursively created in all the imported configs also.`).
WithExample("Add a new profile named 'gcb' targeting the builder 'googleCloudBuild' against the GCP project ID '1234'.", "inspect build-env add googleCloudBuild --profile gcb --projectID 1234 -f skaffold.yaml").
WithFlagAdder(cmdBuildEnvAddGcbFlags).
NoArgs(addGcbBuildEnv)
}

func listBuildEnv(ctx context.Context, out io.Writer) error {
return inspect.PrintBuildEnvsList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat, Modules: inspectFlags.modules, BuildEnvOptions: inspect.BuildEnvOptions{Profiles: inspectFlags.profiles}})
return buildEnv.PrintBuildEnvsList(ctx, out, printBuildEnvsListOptions())
}

func addGcbBuildEnv(ctx context.Context, out io.Writer) error {
return buildEnv.AddGcbBuildEnv(ctx, out, addGcbBuildEnvOptions())
}

func cmdBuildEnvAddFlags(f *pflag.FlagSet) {
f.StringVarP(&buildEnvFlags.profile, "profile", "p", "", `Profile name to add the new build env definition in. If the profile name doesn't exist then the profile will be created in all the target configs. If this flag is not specified then the build env is added to the default pipeline of the target configs.`)
}

func cmdBuildEnvAddGcbFlags(f *pflag.FlagSet) {
f.StringVar(&buildEnvFlags.projectID, "projectId", "", `ID of the Cloud Platform Project.`)
f.Int64Var(&buildEnvFlags.diskSizeGb, "diskSizeGb", 0, `Disk size of the VM that runs the build`)
f.StringVar(&buildEnvFlags.machineType, "machineType", "", `Type of VM that runs the build`)
f.StringVar(&buildEnvFlags.timeout, "timeout", "", `Build timeout (in seconds)`)
f.IntVar(&buildEnvFlags.concurrency, "concurrency", -1, `number of artifacts to build concurrently. 0 means "no-limit"`)
}

func cmdBuildEnvFlags(f *pflag.FlagSet) {
Expand All @@ -52,3 +97,29 @@ func cmdBuildEnvFlags(f *pflag.FlagSet) {
func cmdBuildEnvListFlags(f *pflag.FlagSet) {
f.StringSliceVarP(&inspectFlags.profiles, "profile", "p", nil, `Profile names to activate`)
}

func printBuildEnvsListOptions() inspect.Options {
return inspect.Options{
Filename: inspectFlags.fileName,
OutFormat: inspectFlags.outFormat,
Modules: inspectFlags.modules,
BuildEnvOptions: inspect.BuildEnvOptions{
Profiles: inspectFlags.profiles,
},
}
}
func addGcbBuildEnvOptions() inspect.Options {
return inspect.Options{
Filename: inspectFlags.fileName,
OutFormat: inspectFlags.outFormat,
Modules: inspectFlags.modules,
BuildEnvOptions: inspect.BuildEnvOptions{
Profile: buildEnvFlags.profile,
ProjectID: buildEnvFlags.projectID,
DiskSizeGb: buildEnvFlags.diskSizeGb,
MachineType: buildEnvFlags.machineType,
Timeout: buildEnvFlags.timeout,
Concurrency: buildEnvFlags.concurrency,
},
}
}
3 changes: 2 additions & 1 deletion cmd/skaffold/app/cmd/inspect_modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect"
modules "github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect/modules"
)

func cmdModules() *cobra.Command {
Expand All @@ -39,5 +40,5 @@ func cmdModulesList() *cobra.Command {
}

func listModules(ctx context.Context, out io.Writer) error {
return inspect.PrintModulesList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat})
return modules.PrintModulesList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat})
}
3 changes: 2 additions & 1 deletion cmd/skaffold/app/cmd/inspect_profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/spf13/pflag"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect"
profiles "github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect/profiles"
)

func cmdProfiles() *cobra.Command {
Expand All @@ -43,7 +44,7 @@ func cmdProfilesList() *cobra.Command {
}

func listProfiles(ctx context.Context, out io.Writer) error {
return inspect.PrintProfilesList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat, Modules: inspectFlags.modules, ProfilesOptions: inspect.ProfilesOptions{BuildEnv: inspect.BuildEnv(inspectFlags.buildEnv)}})
return profiles.PrintProfilesList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat, Modules: inspectFlags.modules, ProfilesOptions: inspect.ProfilesOptions{BuildEnv: inspect.BuildEnv(inspectFlags.buildEnv)}})
}

func cmdProfilesFlags(f *pflag.FlagSet) {
Expand Down
87 changes: 58 additions & 29 deletions docs/content/en/api/skaffold.swagger.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/content/en/docs/references/api/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,8 @@ For Cancelled Error code, use range 800 to 850.<br>
| CONFIG_FILE_PATHS_SUBSTITUTION_ERR | 1210 | Failed to substitute absolute file paths in config |
| CONFIG_MULTI_IMPORT_PROFILE_CONFLICT_ERR | 1211 | Same config imported at least twice with different set of profiles |
| CONFIG_PROFILES_NOT_FOUND_ERR | 1212 | Profile selection did not match known profile names |
| INSPECT_UNKNOWN_ERR | 1301 | Catch-all `skaffold inspect` command error |
| INSPECT_BUILD_ENV_ALREADY_EXISTS_ERR | 1302 | Trying to add new build environment that already exists |



Expand Down Expand Up @@ -1065,6 +1067,7 @@ Enum for Suggestion codes
| CONFIG_CHECK_PROFILE_DEFINITION | 704 | Check profile definition in current config |
| CONFIG_CHECK_DEPENDENCY_PROFILES_SELECTION | 705 | Check active profile selection for dependency config |
| CONFIG_CHECK_PROFILE_SELECTION | 706 | Check profile selection flag |
| INSPECT_DEDUP_NEW_BUILD_ENV | 800 | `skaffold inspect` command error suggestion codes |
| OPEN_ISSUE | 900 | Open an issue so this situation can be diagnosed |
| CHECK_CUSTOM_COMMAND | 1000 | Test error suggestion codes |
| FIX_CUSTOM_COMMAND_TIMEOUT | 1001 | |
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type SkaffoldOptions struct {
ProfileAutoActivation bool
DryRun bool
SkipRender bool
SkipConfigDefaults bool

// Add Skaffold-specific labels including runID, deployer labels, etc.
// `CustomLabels` are still applied if this is false. Must only be used in
Expand Down Expand Up @@ -91,7 +92,6 @@ type SkaffoldOptions struct {
RPCHTTPPort int
BuildConcurrency int
MakePathsAbsolute *bool

// TODO(https://github.com/GoogleContainerTools/skaffold/issues/3668):
// remove minikubeProfile from here and instead detect it by matching the
// kubecontext API Server to minikube profiles
Expand Down
113 changes: 113 additions & 0 deletions pkg/skaffold/inspect/buildEnv/add_gcb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package inspect

import (
"context"
"io"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/parser"
latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

func AddGcbBuildEnv(ctx context.Context, out io.Writer, opts inspect.Options) error {
formatter := inspect.OutputFormatter(out, opts.OutFormat)
cfgs, err := inspect.ConfigSetFunc(config.SkaffoldOptions{ConfigurationFile: opts.Filename, ConfigurationFilter: opts.Modules, SkipConfigDefaults: true, MakePathsAbsolute: util.BoolPtr(false)})
if err != nil {
return formatter.WriteErr(err)
}
if opts.Profile == "" {
// empty profile flag implies that the new build env needs to be added to the default pipeline.
// for these cases, don't add the new env definition to any configs imported as dependencies.
cfgs = cfgs.SelectRootConfigs()
for _, cfg := range cfgs {
if cfg.Build.GoogleCloudBuild != nil && (*cfg.Build.GoogleCloudBuild != latestV1.GoogleCloudBuild{}) {
return formatter.WriteErr(inspect.BuildEnvAlreadyExists(inspect.BuildEnvs.GoogleCloudBuild, cfg.SourceFile, ""))
}
cfg.Build.GoogleCloudBuild = constructGcbDefinition(cfg.Build.GoogleCloudBuild, opts.BuildEnvOptions)
cfg.Build.LocalBuild = nil
cfg.Build.Cluster = nil
}
} else {
for _, cfg := range cfgs {
index := -1
for i := range cfg.Profiles {
if cfg.Profiles[i].Name == opts.Profile {
index = i
break
}
}
if index < 0 {
index = len(cfg.Profiles)
cfg.Profiles = append(cfg.Profiles, latestV1.Profile{Name: opts.Profile})
}
if cfg.Profiles[index].Build.GoogleCloudBuild != nil && (*cfg.Profiles[index].Build.GoogleCloudBuild != latestV1.GoogleCloudBuild{}) {
return formatter.WriteErr(inspect.BuildEnvAlreadyExists(inspect.BuildEnvs.GoogleCloudBuild, cfg.SourceFile, opts.Profile))
}
cfg.Profiles[index].Build.GoogleCloudBuild = constructGcbDefinition(cfg.Profiles[index].Build.GoogleCloudBuild, opts.BuildEnvOptions)
cfg.Profiles[index].Build.LocalBuild = nil
cfg.Profiles[index].Build.Cluster = nil

addProfileActivationStanza(cfg, opts.Profile)
}
}
return inspect.MarshalConfigSet(cfgs)
}

func constructGcbDefinition(existing *latestV1.GoogleCloudBuild, opts inspect.BuildEnvOptions) *latestV1.GoogleCloudBuild {
var b latestV1.GoogleCloudBuild
if existing != nil {
b = *existing
}
if opts.Concurrency >= 0 {
b.Concurrency = opts.Concurrency
}
if opts.DiskSizeGb > 0 {
b.DiskSizeGb = opts.DiskSizeGb
}
if opts.MachineType != "" {
b.MachineType = opts.MachineType
}
if opts.ProjectID != "" {
b.ProjectID = opts.ProjectID
}
if opts.Timeout != "" {
b.Timeout = opts.Timeout
}
return &b
}

func addProfileActivationStanza(cfg *parser.SkaffoldConfigEntry, profileName string) {
for i := range cfg.Dependencies {
if cfg.Dependencies[i].GitRepo != nil {
// setup profile activation stanza only for local config dependencies
continue
}
for j := range cfg.Dependencies[i].ActiveProfiles {
if cfg.Dependencies[i].ActiveProfiles[j].Name == profileName {
if !util.StrSliceContains(cfg.Dependencies[i].ActiveProfiles[j].ActivatedBy, profileName) {
cfg.Dependencies[i].ActiveProfiles[j].ActivatedBy = append(cfg.Dependencies[i].ActiveProfiles[j].ActivatedBy, profileName)
}
return
}
}
cfg.Dependencies[i].ActiveProfiles = append(cfg.Dependencies[i].ActiveProfiles, latestV1.ProfileDependency{Name: profileName, ActivatedBy: []string{profileName}})
}
}
Loading

0 comments on commit d9b397e

Please sign in to comment.