Skip to content

Commit d81b9cc

Browse files
authored
Merge pull request #2258 from tanner-bruce/fix-remote-manifests
Fix remoteManifests
2 parents ab8eadc + 76e4e9d commit d81b9cc

File tree

5 files changed

+177
-8
lines changed

5 files changed

+177
-8
lines changed

pkg/skaffold/deploy/kubectl.go

+57
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ limitations under the License.
1717
package deploy
1818

1919
import (
20+
"bytes"
2021
"context"
2122
"io"
23+
"strings"
2224

2325
"github.com/pkg/errors"
2426
"github.com/sirupsen/logrus"
@@ -38,6 +40,7 @@ import (
3840
type KubectlDeployer struct {
3941
*latest.KubectlDeploy
4042

43+
originalImages []build.Artifact
4144
workingDir string
4245
kubectl deploy.CLI
4346
defaultRepo string
@@ -80,6 +83,24 @@ func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []bu
8083
return errors.Wrap(err, "reading manifests")
8184
}
8285

86+
for _, m := range k.RemoteManifests {
87+
manifest, err := k.readRemoteManifest(ctx, m)
88+
if err != nil {
89+
return errors.Wrap(err, "get remote manifests")
90+
}
91+
92+
manifests = append(manifests, manifest)
93+
}
94+
95+
if len(k.originalImages) == 0 {
96+
k.originalImages, err = manifests.GetImages()
97+
if err != nil {
98+
return errors.Wrap(err, "get images from manifests")
99+
}
100+
}
101+
102+
logrus.Debugln("manifests", manifests.String())
103+
83104
if len(manifests) == 0 {
84105
return nil
85106
}
@@ -122,6 +143,22 @@ func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
122143
return errors.Wrap(err, "reading manifests")
123144
}
124145

146+
// pull remote manifests
147+
var rm deploy.ManifestList
148+
for _, m := range k.RemoteManifests {
149+
manifest, err := k.readRemoteManifest(ctx, m)
150+
if err != nil {
151+
return errors.Wrap(err, "get remote manifests")
152+
}
153+
rm = append(rm, manifest)
154+
}
155+
upd, err := rm.ReplaceImages(k.originalImages, k.defaultRepo)
156+
if err != nil {
157+
return errors.Wrap(err, "replacing with originals")
158+
}
159+
if err := k.kubectl.Apply(ctx, out, upd); err != nil {
160+
return errors.Wrap(err, "apply original")
161+
}
125162
if err := k.kubectl.Delete(ctx, out, manifests); err != nil {
126163
return errors.Wrap(err, "delete")
127164
}
@@ -182,3 +219,23 @@ func (k *KubectlDeployer) readManifests(ctx context.Context) (deploy.ManifestLis
182219

183220
return k.kubectl.ReadManifests(ctx, manifests)
184221
}
222+
223+
// readRemoteManifests will try to read manifests from the given kubernetes
224+
// context in the specified namespace and for the specified type
225+
func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) {
226+
var args []string
227+
ns := ""
228+
if parts := strings.Split(name, ":"); len(parts) > 1 {
229+
ns = parts[0]
230+
name = parts[1]
231+
}
232+
args = append(args, name, "-o", "yaml")
233+
234+
var manifest bytes.Buffer
235+
err := k.kubectl.RunInNamespace(ctx, nil, &manifest, "get", ns, args...)
236+
if err != nil {
237+
return nil, errors.Wrap(err, "getting manifest")
238+
}
239+
240+
return manifest.Bytes(), nil
241+
}

pkg/skaffold/deploy/kubectl/images.go

+37-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ limitations under the License.
1717
package kubectl
1818

1919
import (
20-
"github.com/pkg/errors"
21-
"github.com/sirupsen/logrus"
22-
2320
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
2421
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
2522
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
2623
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings"
24+
25+
"github.com/pkg/errors"
26+
"github.com/sirupsen/logrus"
2727
)
2828

2929
// ReplaceImages replaces image names in a list of manifests.
@@ -41,6 +41,38 @@ func (l *ManifestList) ReplaceImages(builds []build.Artifact, defaultRepo string
4141
return updated, nil
4242
}
4343

44+
// GetImages gathers a map of base image names to the image with its tag
45+
func (l *ManifestList) GetImages() ([]build.Artifact, error) {
46+
s := &imageSaver{}
47+
_, err := l.Visit(s)
48+
return s.Images, err
49+
}
50+
51+
type imageSaver struct {
52+
Images []build.Artifact
53+
}
54+
55+
func (is *imageSaver) Matches(key string) bool {
56+
return key == "image"
57+
}
58+
59+
func (is *imageSaver) NewValue(old interface{}) (bool, interface{}) {
60+
image, ok := old.(string)
61+
if !ok {
62+
return false, nil
63+
}
64+
parsed, err := docker.ParseReference(image)
65+
if err != nil {
66+
return false, err
67+
}
68+
69+
is.Images = append(is.Images, build.Artifact{
70+
Tag: image,
71+
ImageName: parsed.BaseName,
72+
})
73+
return false, nil
74+
}
75+
4476
type imageReplacer struct {
4577
defaultRepo string
4678
tagsByImageName map[string]string
@@ -82,6 +114,8 @@ func (r *imageReplacer) NewValue(old interface{}) (bool, interface{}) {
82114
return found, tag
83115
}
84116

117+
// parseAndReplace takes an image from a manifest and if that image matches
118+
// a built image it will update the tag
85119
func (r *imageReplacer) parseAndReplace(image string) (bool, interface{}) {
86120
parsed, err := docker.ParseReference(image)
87121
if err != nil {

pkg/skaffold/deploy/kubectl/visitor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package kubectl
1818

1919
import (
2020
"github.com/pkg/errors"
21-
yaml "gopkg.in/yaml.v2"
21+
"gopkg.in/yaml.v2"
2222
)
2323

2424
// Replacer is used to replace portions of yaml manifests that match a given key.

pkg/skaffold/deploy/kubectl_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,61 @@ func TestKubectlCleanup(t *testing.T) {
270270
}
271271
}
272272

273+
func TestKubectlDeployerRemoteCleanup(t *testing.T) {
274+
tests := []struct {
275+
description string
276+
cfg *latest.KubectlDeploy
277+
command util.Command
278+
}{
279+
{
280+
description: "cleanup success",
281+
cfg: &latest.KubectlDeploy{
282+
RemoteManifests: []string{"pod/leeroy-web"},
283+
},
284+
command: testutil.NewFakeCmd(t).
285+
WithRun("kubectl --context kubecontext --namespace testNamespace get pod/leeroy-web -o yaml").
286+
WithRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -").
287+
WithRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentWebYAML),
288+
},
289+
{
290+
description: "cleanup error",
291+
cfg: &latest.KubectlDeploy{
292+
RemoteManifests: []string{"anotherNamespace:pod/leeroy-web"},
293+
},
294+
command: testutil.NewFakeCmd(t).
295+
WithRun("kubectl --context kubecontext --namespace anotherNamespace get pod/leeroy-web -o yaml").
296+
WithRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -").
297+
WithRunInput("kubectl --context kubecontext --namespace anotherNamespace apply -f -", deploymentWebYAML),
298+
},
299+
}
300+
for _, test := range tests {
301+
testutil.Run(t, "cleanup remote", func(t *testutil.T) {
302+
t.Override(&util.DefaultExecCommand, test.command)
303+
t.NewTempDir().
304+
Write("deployment.yaml", deploymentWebYAML).
305+
Chdir()
306+
307+
k := NewKubectlDeployer(&runcontext.RunContext{
308+
WorkingDir: ".",
309+
Cfg: latest.Pipeline{
310+
Deploy: latest.DeployConfig{
311+
DeployType: latest.DeployType{
312+
KubectlDeploy: test.cfg,
313+
},
314+
},
315+
},
316+
KubeContext: testKubeContext,
317+
Opts: config.SkaffoldOptions{
318+
Namespace: testNamespace,
319+
},
320+
})
321+
err := k.Cleanup(context.Background(), ioutil.Discard)
322+
323+
t.CheckError(false, err)
324+
})
325+
}
326+
}
327+
273328
func TestKubectlRedeploy(t *testing.T) {
274329
testutil.Run(t, "", func(t *testutil.T) {
275330
tmpDir := t.NewTempDir().

pkg/skaffold/kubectl/cli.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ func NewFromRunContext(runCtx *runcontext.RunContext) *CLI {
4444

4545
// Command creates the underlying exec.CommandContext. This allows low-level control of the executed command.
4646
func (c *CLI) Command(ctx context.Context, command string, arg ...string) *exec.Cmd {
47-
args := c.args(command, arg...)
47+
args := c.args(command, "", arg...)
48+
return exec.CommandContext(ctx, "kubectl", args...)
49+
}
50+
51+
// Command creates the underlying exec.CommandContext with namespace. This allows low-level control of the executed command.
52+
func (c *CLI) CommandWithNamespaceArg(ctx context.Context, command string, namespace string, arg ...string) *exec.Cmd {
53+
args := c.args(command, namespace, arg...)
4854
return exec.CommandContext(ctx, "kubectl", args...)
4955
}
5056

@@ -57,6 +63,15 @@ func (c *CLI) Run(ctx context.Context, in io.Reader, out io.Writer, command stri
5763
return util.RunCmd(cmd)
5864
}
5965

66+
// Run shells out kubectl CLI with given namespace
67+
func (c *CLI) RunInNamespace(ctx context.Context, in io.Reader, out io.Writer, command string, namespace string, arg ...string) error {
68+
cmd := c.CommandWithNamespaceArg(ctx, command, namespace, arg...)
69+
cmd.Stdin = in
70+
cmd.Stdout = out
71+
cmd.Stderr = out
72+
return util.RunCmd(cmd)
73+
}
74+
6075
// Run shells out kubectl CLI.
6176
func (c *CLI) RunOut(ctx context.Context, command string, arg ...string) ([]byte, error) {
6277
cmd := c.Command(ctx, command, arg...)
@@ -65,12 +80,20 @@ func (c *CLI) RunOut(ctx context.Context, command string, arg ...string) ([]byte
6580

6681
// args builds an argument list for calling kubectl and consistently
6782
// adds the `--context` and `--namespace` flags.
68-
func (c *CLI) args(command string, arg ...string) []string {
83+
func (c *CLI) args(command string, namespace string, arg ...string) []string {
6984
args := []string{"--context", c.KubeContext}
70-
if c.Namespace != "" {
71-
args = append(args, "--namespace", c.Namespace)
85+
namespace = c.resolveNamespace(namespace)
86+
if namespace != "" {
87+
args = append(args, "--namespace", namespace)
7288
}
7389
args = append(args, command)
7490
args = append(args, arg...)
7591
return args
7692
}
93+
94+
func (c *CLI) resolveNamespace(ns string) string {
95+
if ns != "" {
96+
return ns
97+
}
98+
return c.Namespace
99+
}

0 commit comments

Comments
 (0)