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

Kpt Deployer Deploy() Implementation/Tests #4723

Merged
merged 2 commits into from
Aug 27, 2020
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
45 changes: 39 additions & 6 deletions pkg/skaffold/deploy/kpt.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
Expand Down Expand Up @@ -60,8 +61,40 @@ func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *Kp
}
}

// Deploy hydrates the manifests using kustomizations and kpt functions as described in the render method,
// outputs them to the applyDir, and runs `kpt live apply` against applyDir to create resources in the cluster.
// `kpt live apply` supports automated pruning declaratively via resources in the applyDir.
func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) {
return nil, nil
manifests, err := k.renderManifests(ctx, out, builds)
if err != nil {
return nil, err
}

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

namespaces, err := manifests.CollectNamespaces()
if err != nil {
event.DeployInfoEvent(fmt.Errorf("could not fetch deployed resource namespace. "+
"This might cause port-forward and deploy health-check to fail: %w", err))
}

applyDir, err := k.getApplyDir(ctx)
if err != nil {
return nil, fmt.Errorf("getting applyDir: %w", err)
}

outputRenderedManifests(manifests.String(), filepath.Join(applyDir, "resources.yaml"), out)

cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "apply"}, k.Live.Apply, nil)...)
cmd.Stdout = out
cmd.Stderr = out
if err := util.RunCmd(cmd); err != nil {
return nil, err
}

return namespaces, nil
}

// Dependencies returns a list of files that the deployer depends on. This does NOT include applyDir.
Expand Down Expand Up @@ -91,17 +124,17 @@ func (k *KptDeployer) Dependencies() ([]string, error) {
}

// Cleanup deletes what was deployed by calling `kpt live destroy`.
func (k *KptDeployer) Cleanup(ctx context.Context, _ io.Writer) error {
func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error {
applyDir, err := k.getApplyDir(ctx)
if err != nil {
return fmt.Errorf("getting applyDir: %w", err)
}

cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "destroy"}, nil, nil)...)
out, err := util.RunCmdOut(cmd)
if err != nil {
// Kpt errors are written in STDOUT and surrounded by `\n`.
return fmt.Errorf("kpt live destroy: %s", strings.Trim(string(out), "\n"))
cmd.Stdout = out
cmd.Stderr = out
if err := util.RunCmd(cmd); err != nil {
return err
}

return nil
Expand Down
114 changes: 104 additions & 10 deletions pkg/skaffold/deploy/kpt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,114 @@ import (
)

func TestKpt_Deploy(t *testing.T) {
output := `apiVersion: v1
kind: Pod
metadata:
namespace: default
spec:
containers:
- image: gcr.io/project/image1
name: image1
`
tests := []struct {
description string
expected []string
shouldErr bool
description string
builds []build.Artifact
cfg *latest.KptDeploy
kustomizations map[string]string
commands util.Command
expected []string
shouldErr bool
}{
{
description: "nil",
description: "no manifest",
cfg: &latest.KptDeploy{
Dir: ".",
},
commands: testutil.
CmdRunOut("kpt fn source .", ``).
AndRunOut("kpt fn sink .pipeline", ``).
AndRunOut("kpt fn run .pipeline --dry-run", ``),
},
{
description: "invalid manifest",
cfg: &latest.KptDeploy{
Dir: ".",
},
commands: testutil.
CmdRunOut("kpt fn source .", ``).
AndRunOut("kpt fn sink .pipeline", ``).
AndRunOut("kpt fn run .pipeline --dry-run", `foo`),
shouldErr: true,
},
{
description: "invalid user specified applyDir",
cfg: &latest.KptDeploy{
Dir: ".",
ApplyDir: "invalid_path",
},
commands: testutil.
CmdRunOut("kpt fn source .", ``).
AndRunOut("kpt fn sink .pipeline", ``).
AndRunOut("kpt fn run .pipeline --dry-run", output),
shouldErr: true,
},
{
description: "kustomization and specified kpt fn",
cfg: &latest.KptDeploy{
Dir: ".",
Fn: latest.KptFn{FnPath: "kpt-func.yaml"},
ApplyDir: "valid_path",
},
kustomizations: map[string]string{"Kustomization": `resources:
- foo.yaml`},
commands: testutil.
CmdRunOut("kpt fn source .", ``).
AndRunOut("kpt fn sink .pipeline", ``).
AndRunOut("kustomize build -o .pipeline .", ``).
AndRunOut("kpt fn run .pipeline --dry-run --fn-path kpt-func.yaml", output).
AndRun("kpt live apply valid_path"),
expected: []string{"default"},
},
{
description: "kpt live apply fails",
cfg: &latest.KptDeploy{
Dir: ".",
},
commands: testutil.
CmdRunOut("kpt fn source .", ``).
AndRunOut("kpt fn sink .pipeline", ``).
AndRunOut("kpt fn run .pipeline --dry-run", output).
AndRunOut("kpt live init .kpt-hydrated", ``).
AndRunErr("kpt live apply .kpt-hydrated", errors.New("BUG")),
shouldErr: true,
},
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
k := NewKptDeployer(&runcontext.RunContext{}, nil)
res, err := k.Deploy(context.Background(), nil, nil)
t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res)
t.Override(&util.DefaultExecCommand, test.commands)
tmpDir := t.NewTempDir().Chdir()

tmpDir.WriteFiles(test.kustomizations)

k := NewKptDeployer(&runcontext.RunContext{
Cfg: latest.Pipeline{
Deploy: latest.DeployConfig{
DeployType: latest.DeployType{
KptDeploy: test.cfg,
},
},
},
}, nil)

if k.ApplyDir == "valid_path" {
// 0755 is a permission setting where the owner can read, write, and execute.
// Others can read and execute but not modify the directory.
os.Mkdir(k.ApplyDir, 0755)
}

_, err := k.Deploy(context.Background(), ioutil.Discard, test.builds)

t.CheckError(test.shouldErr, err)
})
}
}
Expand Down Expand Up @@ -194,19 +288,19 @@ func TestKpt_Cleanup(t *testing.T) {
{
description: "valid user specified applyDir w/o template resource",
applyDir: "valid_path",
commands: testutil.CmdRunOutErr("kpt live destroy valid_path", "", errors.New("BUG")),
commands: testutil.CmdRunErr("kpt live destroy valid_path", errors.New("BUG")),
shouldErr: true,
},
{
description: "valid user specified applyDir w/ template resource (emulated)",
applyDir: "valid_path",
commands: testutil.CmdRunOut("kpt live destroy valid_path", ""),
commands: testutil.CmdRun("kpt live destroy valid_path"),
},
{
description: "unspecified applyDir",
commands: testutil.
CmdRunOut("kpt live init .kpt-hydrated", "").
AndRunOut("kpt live destroy .kpt-hydrated", ""),
AndRun("kpt live destroy .kpt-hydrated"),
},
}
for _, test := range tests {
Expand Down