-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add labels to all k8s objects deployed by skaffold #644
Conversation
pkg/skaffold/deploy/helm.go
Outdated
} | ||
labelDeployResults(deployResults) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid having to run this in each deployer, I would suggest that we change the deploy interface back to
Deploy(ctx context.Context, out io.Writer, builds []build.Build) (DeployResult, error)
That maybe looks like
type DeployResult struct {
deployedArtifacts []DeployedArtifact
}
type DeployedArtifact struct {
GVK string
obj *runtime.Obj
}
The high level flow would be
func run() {
buildResults := builder.build()
deployResults := deployer.deploy(buildResults)
label(cfg, deployResults)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had played around with having this live in the runner, but decided to go with putting it in each deployer for two reasons:
- the runner will need to know less about what happened in its individually managed steps, and
- labeling should be coupled to deploying, since we can't label unless we've successfully deployed.
That said, I can see arguments for moving it to the runner as well. If we add more deployers that's more boilerplate we have to remember to add, and the runner has a lot more information about the overall skaffold run which could be useful labeling information. Since the runner has all the skaffold config info, I think that's a strong enough argument for moving it back, so I'll go ahead with it and see how it looks.
pkg/skaffold/deploy/deploy.go
Outdated
// DeployResult is a holder struct encapsulating the deployed k8s objects | ||
type DeployResult struct { | ||
obj *runtime.Object | ||
namespace string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The namespace is part of of the runtime.Object I believe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately it isn't always, helm can't be trusted to put it where it should be, among many other things that it should do but doesn't
pkg/skaffold/deploy/deploy.go
Outdated
type DeployResult struct { | ||
obj *runtime.Object | ||
namespace string | ||
deployer string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can infer this from the config. I'm OK with adding a Name() string
requirement to the deploy interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed as per my comment above
pkg/skaffold/deploy/label.go
Outdated
return "default" | ||
} | ||
|
||
func updateRuntimeObject(client clientgo.Interface, labels map[string]string, res Artifact) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the dynamic client should make this easy.
https://github.com/kubernetes/client-go/blob/master/dynamic/interface.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like client-go itself is discouraging use of the dynamic client: kubernetes/client-go#394. Even though it looks a little messy I think sticking to the typed APIs is safer and probably a better idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use it anyways. Lets keep it out of scope for this PR though.
pkg/skaffold/deploy/label.go
Outdated
const tries int = 3 | ||
const sleeptime time.Duration = 300 * time.Millisecond | ||
|
||
func LabelDeployResults(labels map[string]string, results []Artifact) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since labels come from the runner, this should be in the runner package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to runner/labels
pkg/skaffold/deploy/label.go
Outdated
return "default" | ||
} | ||
|
||
func updateRuntimeObject(client clientgo.Interface, labels map[string]string, res Artifact) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a TODO here to get rid of this in favor of the dynamic client or something else
pkg/skaffold/build/tag/git_commit.go
Outdated
@@ -32,6 +34,12 @@ import ( | |||
type GitCommit struct { | |||
} | |||
|
|||
func (c *GitCommit) Labels() map[string]string { | |||
return map[string]string{ | |||
constants.Labels.TagPolicy: "git_commit", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consistency gitCommit
pkg/skaffold/config/options.go
Outdated
func (opts *SkaffoldOptions) Labels() map[string]string { | ||
labels := map[string]string{} | ||
if opts.CustomTag != "" { | ||
labels["custom_tag"] = "true" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove this
Add
- namespace
- profiles
- cleanup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
profiles is a little special because this will just contain the list of all profiles specified on the CLI, but not which one was actually used to create each k8s API object. was planning on opening an issue to add support for profile labeling outside this PR
if err != nil { | ||
return err | ||
dRes, err := w.Deployer.Deploy(ctx, out, builds) | ||
if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this error goes unchecked
dRes, err := w.Deployer.Deploy
if err != nil {
return nil, err
}
fmt.Fprintln
return dRes, nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, got the logic slightly wrong here. that's equivalent to
dRes, err := w.Deployer.Deploy
if err == nil {
fmt.Fprintln
}
return dRes, err
which is what I was trying to write.
// Retrieve info about all releases using helm get | ||
// Skaffold labels will be applied to each deployed k8s object | ||
// Since helm isn't always consistent with retrieving results, don't return errors here | ||
func (h *HelmDeployer) getDeployResults(namespace string, release string) []Artifact { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a test for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
pkg/skaffold/deploy/deploy.go
Outdated
// Artifact contains all information about a completed deployment | ||
type Artifact struct { | ||
obj *runtime.Object | ||
namespace string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we need namespace here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the namespace used to do a deployment doesn't always match the namespace in the deployer, and we need access to it when updating the API objects with the labels
pkg/skaffold/constants/constants.go
Outdated
DefaultLabels map[string]string | ||
}{ | ||
DefaultLabels: map[string]string{ | ||
"deployed_with": "skaffold", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: hyphen instead of underscore
pkg/skaffold/build/local.go
Outdated
@@ -64,6 +64,12 @@ func NewLocalBuilder(cfg *v1alpha2.BuildConfig, kubeContext string) (*LocalBuild | |||
return l, nil | |||
} | |||
|
|||
func (l *LocalBuilder) Labels() map[string]string { | |||
return map[string]string{ | |||
constants.Labels.Builder: "local", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
add some context from the docker api
"docker-api-version": api.ServerVersion(ctx)
func (cb *GoogleCloudBuilder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*v1alpha2.Artifact) ([]Build, error) { | ||
func (cb *GoogleCloudBuilder) Labels() map[string]string { | ||
return map[string]string{ | ||
constants.Labels.Builder: "GoogleCloudBuilder", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
google-cloud-builder
93106ed
to
a52436d
Compare
pkg/skaffold/deploy/helm.go
Outdated
|
||
func (h *HelmDeployer) getReleaseInfo(release string) (*bufio.Reader, error) { | ||
var releaseInfo bytes.Buffer | ||
out := bufio.NewWriter(&releaseInfo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't need this bufio.NewWriter, you can use a var releaseInfo bytes.Buffer
directly as a reader/writer:
https://golang.org/pkg/bytes/#Buffer.Write
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, thanks
pkg/skaffold/deploy/helm.go
Outdated
break | ||
} | ||
if err != nil { | ||
logrus.Infof("error parsing helm deployment: %s", err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we have a continue
here to avoid trying to parse an invalid or nil doc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call, added
func parseManifestsForDeploys(manifests manifestList) ([]Artifact, error) { | ||
results := []Artifact{} | ||
for _, manifest := range manifests { | ||
b := bufio.NewReader(bytes.NewReader(manifest)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you wrap this in bufio?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the YAMLReader from apimachinery requires a bufio.Reader instead of an io.Reader: https://github.com/kubernetes/apimachinery/blob/master/pkg/util/yaml/decoder.go#L262
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see I was too slow. Added couple of nits maybe for next time.
@@ -50,7 +51,7 @@ func NewCmdDeploy(out io.Writer) *cobra.Command { | |||
func runDeploy(out io.Writer, filename string) error { | |||
ctx := context.Background() | |||
|
|||
runner, _, err := newRunner(filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd think runner is short enough.
@@ -40,6 +42,12 @@ func NewDateTimeTagger(format, timezone string) Tagger { | |||
} | |||
} | |||
|
|||
func (tagger *dateTimeTagger) Labels() map[string]string { | |||
return map[string]string{ | |||
constants.Labels.TagPolicy: "dateTimeTagger", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this could be just dateTime
to be consistent
@@ -40,8 +41,14 @@ func NewEnvTemplateTagger(t string) (Tagger, error) { | |||
}, nil | |||
} | |||
|
|||
func (t *envTemplateTagger) Labels() map[string]string { | |||
return map[string]string{ | |||
constants.Labels.TagPolicy: "envTemplateTagger", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this could be just envTemplate
to be consistent
const sleeptime time.Duration = 300 * time.Millisecond | ||
|
||
//nolint | ||
func LabelDeployResults(labels map[string]string, results []deploy.Artifact) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: how about ApplyLabels
?
This adds labels to all kubernetes objects deployed by Skaffold.
By default, the labeldeployed_with:skaffold
is added to all objects, as well as askaffold_deploy_client:<client>
with the deployer used in skaffold. We can add more labels or change these as we see fit.Each
Builder
,Deployer
, andTagger
implementation now exposes its own set of labels to theRunner
, which will aggregate all labels and send those to the deployed objects on each run.We should consider adding labels for individual profiles as well, possibly in a follow-up PR.
Also, as a small non-functional change, I changed
build.Build
tobuild.BuildArtifact
to matchdeploy.DeployArtifact
.Fixes #98