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
1 change: 1 addition & 0 deletions cmd/skaffold/app/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ func NewSkaffoldCommand(out, errOut io.Writer) *cobra.Command {
rootCmd.AddCommand(NewCmdCredits())
rootCmd.AddCommand(NewCmdSchema())
rootCmd.AddCommand(NewCmdFilter())
rootCmd.AddCommand(NewCmdExec())

rootCmd.AddCommand(NewCmdGeneratePipeline())
rootCmd.AddCommand(NewCmdSurvey())
Expand Down
116 changes: 116 additions & 0 deletions cmd/skaffold/app/cmd/exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
Copyright 2023 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 cmd

import (
"context"
"errors"
"io"

"github.com/spf13/cobra"

"github.com/GoogleContainerTools/skaffold/v2/cmd/skaffold/app/tips"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/graph"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/output/log"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/runner"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/util"
)

// NewCmdExec describes the CLI command to execute a custom action.
func NewCmdExec() *cobra.Command {
return NewCmd("exec").
WithDescription("Execute a custom action").
WithExample("Execute a defined action", "exec <action-name>").
WithExample("Execute a defined action that uses an image built from Skaffold. First, build the images", "build --file-output=build.json").
WithExample("Then use the built artifacts", "exec <action-name> --build-artifacts=build.json").
WithCommonFlags().
WithArgs(func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
log.Entry(context.TODO()).Errorf("`exec` requires exactly one action to execute")
return errors.New("`exec` requires exactly one action to execute")
}
return nil
}, doExec)
}

func doExec(ctx context.Context, out io.Writer, args []string) error {
return withRunner(ctx, out, func(r runner.Runner, configs []util.VersionedConfig) error {
buildArtifacts, err := getBuildArtifactsAndSetTagsForAction(configs, r.ApplyDefaultRepo, args[0])
if err != nil {
tips.PrintUseBuildAndExec(out)
return err
}
return r.Exec(ctx, out, buildArtifacts, args[0])
})
}

func getBuildArtifactsAndSetTagsForAction(configs []util.VersionedConfig, defaulterFn DefaultRepoFn, action string) ([]graph.Artifact, error) {
imgs := getActionImgs(action, configs)

if len(imgs) == 0 {
return nil, nil
}

fromBuildArtifactsFile := getArtifactsFromBuildArtifactsFile(imgs)

// We only use the images from previous builds, read from the --build-artifacts flag.
// `exec` itself does not perform a build, so we don't care about the configuration in the build stanza.
buildArtifacts, err := mergeBuildArtifacts(fromBuildArtifactsFile, preBuiltImages.Artifacts(), []*latest.Artifact{})
if err != nil {
return nil, err
}

return applyDefaultRepoToArtifacts(buildArtifacts, defaulterFn)
}

func getArtifactsFromBuildArtifactsFile(actionImgs map[string]bool) (artifacts []graph.Artifact) {
allArtifacts := fromBuildOutputFile.BuildArtifacts()

for _, a := range allArtifacts {
if actionImgs[a.ImageName] {
artifacts = append(artifacts, a)
}
}

return
}

func getActionImgs(action string, configs []util.VersionedConfig) map[string]bool {
var allActions []latest.Action
imgs := map[string]bool{}

for _, cfg := range configs {
allActions = append(allActions, cfg.(*latest.SkaffoldConfig).CustomActions...)
}

var actionCfg *latest.Action = nil
for _, a := range allActions {
if a.Name == action {
actionCfg = &a
break
}
}

if actionCfg != nil {
for _, c := range actionCfg.Containers {
imgs[c.Image] = true
}
}

return imgs
}
22 changes: 11 additions & 11 deletions cmd/skaffold/app/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ var flagRegistry = []Flag{
Value: &opts.Profiles,
DefValue: []string{},
FlagAddMethod: "StringSliceVar",
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "apply", "test", "verify"},
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "apply", "test", "verify", "exec"},
},
{
Name: "namespace",
Expand All @@ -122,7 +122,7 @@ var flagRegistry = []Flag{
Value: &opts.DefaultRepo,
DefValue: nil,
FlagAddMethod: "Var",
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "verify"},
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "verify", "exec"},
},
{
Name: "cache-artifacts",
Expand Down Expand Up @@ -196,7 +196,7 @@ var flagRegistry = []Flag{
Value: &opts.EventLogFile,
DefValue: "",
FlagAddMethod: "StringVar",
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "render", "test", "apply", "verify"},
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "render", "test", "apply", "verify", "exec"},
},
{
Name: "last-log-file",
Expand All @@ -213,15 +213,15 @@ var flagRegistry = []Flag{
Value: &opts.RPCPort,
DefValue: nil,
FlagAddMethod: "Var",
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply"},
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply", "exec"},
},
{
Name: "rpc-http-port",
Usage: "tcp port to expose the Skaffold API over HTTP REST",
Value: &opts.RPCHTTPPort,
DefValue: nil,
FlagAddMethod: "Var",
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply"},
DefinedOn: []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply", "exec"},
},
{
Name: "label",
Expand Down Expand Up @@ -310,7 +310,7 @@ var flagRegistry = []Flag{
},
NoOptDefVal: "true", // uses the settings from when --port-forward was boolean
FlagAddMethod: "Var",
DefinedOn: []string{"dev", "run", "deploy", "debug", "verify"},
DefinedOn: []string{"dev", "run", "deploy", "debug", "verify", "exec"},
IsEnum: true,
},
{
Expand Down Expand Up @@ -430,7 +430,7 @@ var flagRegistry = []Flag{
Value: &opts.ProfileAutoActivation,
DefValue: true,
FlagAddMethod: "BoolVar",
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify"},
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify", "exec"},
IsEnum: true,
},
{
Expand All @@ -448,7 +448,7 @@ var flagRegistry = []Flag{
Value: &opts.PropagateProfiles,
DefValue: true,
FlagAddMethod: "BoolVar",
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify"},
DefinedOn: []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify", "exec"},
IsEnum: true,
},
{
Expand Down Expand Up @@ -549,7 +549,7 @@ var flagRegistry = []Flag{
Value: &opts.VerifyEnvFile,
DefValue: "",
FlagAddMethod: "StringVar",
DefinedOn: []string{"verify"},
DefinedOn: []string{"verify", "exec"},
},
{
Name: "wait-for-deletions-delay",
Expand Down Expand Up @@ -595,7 +595,7 @@ The build result from a previous 'skaffold build --file-output' run can be used
Value: &fromBuildOutputFile,
DefValue: "",
FlagAddMethod: "Var",
DefinedOn: []string{"deploy", "render", "test", "verify"},
DefinedOn: []string{"deploy", "render", "test", "verify", "exec"},
},

{
Expand Down Expand Up @@ -679,7 +679,7 @@ The build result from a previous 'skaffold build --file-output' run can be used
Value: &opts.VerifyDockerNetwork,
DefValue: "",
FlagAddMethod: "StringVar",
DefinedOn: []string{"verify"},
DefinedOn: []string{"verify", "exec"},
},
{
Name: "enable-platform-node-affinity",
Expand Down
6 changes: 6 additions & 0 deletions cmd/skaffold/app/tips/tips.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ func PrintUseRunVsDeploy(out io.Writer) {
printTip(out, "or [skaffold run] instead, to let Skaffold build, tag and deploy artifacts.")
}

// PrintUseBuildAndExec prints tip to use artifacts from previous build in skaffold exec.
func PrintUseBuildAndExec(out io.Writer) {
printTip(out, "Check all the images have a tag assigned:")
printTip(out, "run [skaffold exec] with [--build-artifacts <file-output>] for running an action using images from a previous build")
}

func printTip(out io.Writer, message string) {
output.Green.Fprintln(out, message)
}
38 changes: 38 additions & 0 deletions docs-v1/content/en/api-v2/skaffold.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,45 @@
},
"cloudRunReadyEvent": {
"$ref": "#/definitions/v2CloudRunReadyEvent"
},
"execEvent": {
"$ref": "#/definitions/v2ExecSubtaskEvent"
}
},
"description": "`Event` describes an event in the Skaffold process.\nIt is one of MetaEvent, BuildEvent, TestEvent, DeployEvent, PortEvent, StatusCheckEvent, ResourceStatusCheckEvent, FileSyncEvent, or DebuggingContainerEvent."
},
"v2ExecState": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "Status of the current exec"
},
"statusCode": {
"$ref": "#/definitions/enumsStatusCode",
"description": "ExecState status code"
}
},
"description": "`ExecState` describes the state of the current exec"
},
"v2ExecSubtaskEvent": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"task_id": {
"type": "string"
},
"status": {
"type": "string"
},
"actionableErr": {
"$ref": "#/definitions/v2ActionableErr"
}
},
"description": "`ExecSubtaskEvent` represents the status of an exec, and is emitted by Skaffold\nanytime an exec starts or completes, successfully or not."
},
"v2FileSyncEvent": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1198,6 +1233,9 @@
},
"verifyState": {
"$ref": "#/definitions/v2VerifyState"
},
"execState": {
"$ref": "#/definitions/v2ExecState"
}
},
"description": "`State` represents the current state of the Skaffold components"
Expand Down
37 changes: 37 additions & 0 deletions docs-v1/content/en/docs/references/api-v2/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,42 @@ It is one of MetaEvent, BuildEvent, TestEvent, DeployEvent, PortEvent, StatusChe
| renderEvent | [RenderSubtaskEvent](#proto.v2.RenderSubtaskEvent) | | describes if the render has started, is in progress or is complete. |
| verifyEvent | [VerifySubtaskEvent](#proto.v2.VerifySubtaskEvent) | | describes if the render has started, is in progress or is complete. |
| cloudRunReadyEvent | [CloudRunReadyEvent](#proto.v2.CloudRunReadyEvent) | | describes a deployed Cloud Run service. |
| execEvent | [ExecSubtaskEvent](#proto.v2.ExecSubtaskEvent) | | describes if the exec has started, is in progress or is complete. |







<a name="proto.v2.ExecState"></a>
#### ExecState
`ExecState` describes the state of the current exec


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| status | [string](#string) | | Status of the current exec |
| statusCode | [proto.enums.StatusCode](#proto.enums.StatusCode) | | ExecState status code |







<a name="proto.v2.ExecSubtaskEvent"></a>
#### ExecSubtaskEvent
`ExecSubtaskEvent` represents the status of an exec, and is emitted by Skaffold
anytime an exec starts or completes, successfully or not.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | id of the subtask which will be used in SkaffoldLog |
| task_id | [string](#string) | | id of the task of skaffold that this event came from |
| status | [string](#string) | | exec status oneof: InProgress, Completed, Failed |
| actionableErr | [ActionableErr](#proto.v2.ActionableErr) | | actionable error message |



Expand Down Expand Up @@ -636,6 +672,7 @@ log level |
| testState | [TestState](#proto.v2.TestState) | | |
| renderState | [RenderState](#proto.v2.RenderState) | | |
| verifyState | [VerifyState](#proto.v2.VerifyState) | | |
| execState | [ExecState](#proto.v2.ExecState) | | |



Expand Down
38 changes: 38 additions & 0 deletions docs-v2/content/en/api-v2/skaffold.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,45 @@
},
"cloudRunReadyEvent": {
"$ref": "#/definitions/v2CloudRunReadyEvent"
},
"execEvent": {
"$ref": "#/definitions/v2ExecSubtaskEvent"
}
},
"description": "`Event` describes an event in the Skaffold process.\nIt is one of MetaEvent, BuildEvent, TestEvent, DeployEvent, PortEvent, StatusCheckEvent, ResourceStatusCheckEvent, FileSyncEvent, or DebuggingContainerEvent."
},
"v2ExecState": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "Status of the current exec"
},
"statusCode": {
"$ref": "#/definitions/enumsStatusCode",
"description": "ExecState status code"
}
},
"description": "`ExecState` describes the state of the current exec"
},
"v2ExecSubtaskEvent": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"task_id": {
"type": "string"
},
"status": {
"type": "string"
},
"actionableErr": {
"$ref": "#/definitions/v2ActionableErr"
}
},
"description": "`ExecSubtaskEvent` represents the status of an exec, and is emitted by Skaffold\nanytime an exec starts or completes, successfully or not."
},
"v2FileSyncEvent": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1198,6 +1233,9 @@
},
"verifyState": {
"$ref": "#/definitions/v2VerifyState"
},
"execState": {
"$ref": "#/definitions/v2ExecState"
}
},
"description": "`State` represents the current state of the Skaffold components"
Expand Down
Loading