Switch to github.com/golang/dep for vendoring

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-01-31 16:45:59 -08:00
parent d6ab91be27
commit 8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions

77
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/BUILD generated vendored Normal file
View file

@ -0,0 +1,77 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"helper.go",
"set.go",
"set_image.go",
"set_resources.go",
"set_selector.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//vendor:github.com/spf13/cobra",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apimachinery/pkg/util/validation",
],
)
go_test(
name = "go_default_test",
srcs = [
"set_image_test.go",
"set_selector_test.go",
"set_test.go",
],
data = [
"//examples:config",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

160
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/helper.go generated vendored Normal file
View file

@ -0,0 +1,160 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"fmt"
"io"
"strings"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/util/strategicpatch"
)
// selectContainers allows one or more containers to be matched against a string or wildcard
func selectContainers(containers []api.Container, spec string) ([]*api.Container, []*api.Container) {
out := []*api.Container{}
skipped := []*api.Container{}
for i, c := range containers {
if selectString(c.Name, spec) {
out = append(out, &containers[i])
} else {
skipped = append(skipped, &containers[i])
}
}
return out, skipped
}
// handlePodUpdateError prints a more useful error to the end user when mutating a pod.
func handlePodUpdateError(out io.Writer, err error, resource string) {
if statusError, ok := err.(*errors.StatusError); ok && errors.IsInvalid(err) {
errorDetails := statusError.Status().Details
if errorDetails.Kind == "Pod" {
all, match := true, false
for _, cause := range errorDetails.Causes {
if cause.Field == "spec" && strings.Contains(cause.Message, "may not update fields other than") {
fmt.Fprintf(out, "error: may not update %s in pod %q directly\n", resource, errorDetails.Name)
match = true
} else {
all = false
}
}
if all && match {
return
}
} else {
if ok := kcmdutil.PrintErrorWithCauses(err, out); ok {
return
}
}
}
fmt.Fprintf(out, "error: %v\n", err)
}
// selectString returns true if the provided string matches spec, where spec is a string with
// a non-greedy '*' wildcard operator.
// TODO: turn into a regex and handle greedy matches and backtracking.
func selectString(s, spec string) bool {
if spec == "*" {
return true
}
if !strings.Contains(spec, "*") {
return s == spec
}
pos := 0
match := true
parts := strings.Split(spec, "*")
for i, part := range parts {
if len(part) == 0 {
continue
}
next := strings.Index(s[pos:], part)
switch {
// next part not in string
case next < pos:
fallthrough
// first part does not match start of string
case i == 0 && pos != 0:
fallthrough
// last part does not exactly match remaining part of string
case i == (len(parts)-1) && len(s) != (len(part)+next):
match = false
break
default:
pos = next
}
}
return match
}
// Patch represents the result of a mutation to an object.
type Patch struct {
Info *resource.Info
Err error
Before []byte
After []byte
Patch []byte
}
// patchFn is a function type that accepts an info object and returns a byte slice.
// Implementations of patchFn should update the object and return it encoded.
type patchFn func(*resource.Info) ([]byte, error)
// CalculatePatch calls the mutation function on the provided info object, and generates a strategic merge patch for
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
// This function returns whether the mutation function made any change in the original object.
func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool {
patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object)
patch.After, patch.Err = mutateFn(patch.Info)
if patch.Err != nil {
return true
}
if patch.After == nil {
return false
}
// TODO: should be via New
versioned, err := patch.Info.Mapping.ConvertToVersion(patch.Info.Object, patch.Info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
patch.Err = err
return true
}
patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned)
return true
}
// CalculatePatches calculates patches on each provided info object. If the provided mutateFn
// makes no change in an object, the object is not included in the final list of patches.
func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn patchFn) []*Patch {
var patches []*Patch
for _, info := range infos {
patch := &Patch{Info: info}
if CalculatePatch(patch, encoder, mutateFn) {
patches = append(patches, patch)
}
}
return patches
}

48
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/set.go generated vendored Normal file
View file

@ -0,0 +1,48 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"io"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
set_long = templates.LongDesc(`
Configure application resources
These commands help you make changes to existing application resources.`)
)
func NewCmdSet(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "set SUBCOMMAND",
Short: "Set specific features on objects",
Long: set_long,
Run: cmdutil.DefaultSubCommandRun(err),
}
// add subcommands
cmd.AddCommand(NewCmdImage(f, out, err))
cmd.AddCommand(NewCmdResources(f, out, err))
cmd.AddCommand(NewCmdSelector(f, out))
return cmd
}

View file

@ -0,0 +1,271 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"fmt"
"io"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
// ImageOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type ImageOptions struct {
resource.FilenameOptions
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Infos []*resource.Info
Encoder runtime.Encoder
Selector string
Out io.Writer
Err io.Writer
DryRun bool
ShortOutput bool
All bool
Record bool
Output string
ChangeCause string
Local bool
Cmd *cobra.Command
ResolveImage func(in string) (string, error)
PrintObject func(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
Resources []string
ContainerImages map[string]string
}
var (
image_resources = `
pod (po), replicationcontroller (rc), deployment (deploy), daemonset (ds), job, replicaset (rs)`
image_long = templates.LongDesc(`
Update existing container image(s) of resources.
Possible resources include (case insensitive):
` + image_resources)
image_example = templates.Examples(`
# Set a deployment's nginx container image to 'nginx:1.9.1', and its busybox container image to 'busybox'.
kubectl set image deployment/nginx busybox=busybox nginx=nginx:1.9.1
# Update all deployments' and rc's nginx container's image to 'nginx:1.9.1'
kubectl set image deployments,rc nginx=nginx:1.9.1 --all
# Update image of all containers of daemonset abc to 'nginx:1.9.1'
kubectl set image daemonset abc *=nginx:1.9.1
# Print result (in yaml format) of updating nginx container image from local file, without hitting the server
kubectl set image -f path/to/file.yaml nginx=nginx:1.9.1 --local -o yaml`)
)
func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &ImageOptions{
Out: out,
Err: err,
}
cmd := &cobra.Command{
Use: "image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N",
Short: "Update image of a pod template",
Long: image_long,
Example: image_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
},
}
cmdutil.AddPrinterFlags(cmd)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", false, "select all resources in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
cmd.Flags().BoolVar(&options.Local, "local", false, "If true, set image will NOT contact api-server but run locally.")
cmdutil.AddRecordFlag(cmd)
cmdutil.AddDryRunFlag(cmd)
return cmd
}
func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.Mapper, o.Typer = f.Object()
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.Encoder = f.JSONEncoder()
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command()
o.PrintObject = f.PrintObject
o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.Output = cmdutil.GetFlagString(cmd, "output")
o.ResolveImage = f.ResolveImage
o.Cmd = cmd
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
o.Resources, o.ContainerImages, err = getResourcesAndImages(args)
if err != nil {
return err
}
builder := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
Flatten()
if !o.Local {
builder = builder.
SelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...).
Latest()
}
o.Infos, err = builder.Do().Infos()
if err != nil {
return err
}
return nil
}
func (o *ImageOptions) Validate() error {
errors := []error{}
if len(o.Resources) < 1 && cmdutil.IsFilenameEmpty(o.Filenames) {
errors = append(errors, fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>"))
}
if len(o.ContainerImages) < 1 {
errors = append(errors, fmt.Errorf("at least one image update is required"))
} else if len(o.ContainerImages) > 1 && hasWildcardKey(o.ContainerImages) {
errors = append(errors, fmt.Errorf("all containers are already specified by *, but saw more than one container_name=container_image pairs"))
}
return utilerrors.NewAggregate(errors)
}
func (o *ImageOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
transformed := false
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
for name, image := range o.ContainerImages {
var (
containerFound bool
err error
resolved string
)
// Find the container to update, and update its image
for i, c := range spec.Containers {
if c.Name == name || name == "*" {
containerFound = true
if len(resolved) == 0 {
if resolved, err = o.ResolveImage(image); err != nil {
allErrs = append(allErrs, fmt.Errorf("error: unable to resolve image %q for container %q: %v", image, name, err))
// Do not loop again if the image resolving failed for wildcard case as we
// will report the same error again for the next container.
if name == "*" {
break
}
continue
}
}
spec.Containers[i].Image = resolved
// Perform updates
transformed = true
}
}
// Add a new container if not found
if !containerFound {
allErrs = append(allErrs, fmt.Errorf("error: unable to find container named %q", name))
}
}
return nil
})
if transformed && err == nil {
return runtime.Encode(o.Encoder, info.Object)
}
return nil, err
})
for _, patch := range patches {
info := patch.Info
if patch.Err != nil {
allErrs = append(allErrs, fmt.Errorf("error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err))
continue
}
// no changes
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
continue
}
if o.PrintObject != nil && (o.Local || o.DryRun) {
return o.PrintObject(o.Cmd, o.Mapper, info.Object, o.Out)
}
// patch the change
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch.Patch)
if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch image update to pod template: %v\n", err))
continue
}
info.Refresh(obj, true)
// record this change (for rollout history)
if o.Record || cmdutil.ContainsChangeCause(info) {
if patch, err := cmdutil.ChangeResourcePatch(info, o.ChangeCause); err == nil {
if obj, err = resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch); err != nil {
fmt.Fprintf(o.Err, "WARNING: changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err)
}
}
}
info.Refresh(obj, true)
if len(o.Output) > 0 {
return o.PrintObject(o.Cmd, o.Mapper, obj, o.Out)
}
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "image updated")
}
return utilerrors.NewAggregate(allErrs)
}
// getResourcesAndImages retrieves resources and container name:images pair from given args
func getResourcesAndImages(args []string) (resources []string, containerImages map[string]string, err error) {
pairType := "image"
resources, imageArgs, err := cmdutil.GetResourcesAndPairs(args, pairType)
if err != nil {
return
}
containerImages, _, err = cmdutil.ParsePairs(imageArgs, pairType, false)
return
}
func hasWildcardKey(containerImages map[string]string) bool {
_, ok := containerImages["*"]
return ok
}

View file

@ -0,0 +1,68 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"bytes"
"net/http"
"strings"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/restclient/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
func TestImageLocal(t *testing.T) {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
return nil, nil
}),
}
tf.Namespace = "test"
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdImage(f, buf, buf)
cmd.SetOutput(buf)
cmd.Flags().Set("output", "name")
tf.Printer, _, _ = cmdutil.PrinterForCommand(cmd)
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}},
Out: buf,
Local: true}
err := opts.Complete(f, cmd, []string{"cassandra=thingy"})
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.Run()
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set image: %s", buf.String())
}
}

View file

@ -0,0 +1,245 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"fmt"
"io"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/kubernetes/pkg/api"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
var (
resources_long = templates.LongDesc(`
Specify compute resource requirements (cpu, memory) for any resource that defines a pod template. If a pod is successfully scheduled, it is guaranteed the amount of resource requested, but may burst up to its specified limits.
for each compute resource, if a limit is specified and a request is omitted, the request will default to the limit.
Possible resources include (case insensitive): %s.`)
resources_example = templates.Examples(`
# Set a deployments nginx container cpu limits to "200m" and memory to "512Mi"
kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
# Set the resource request and limits for all containers in nginx
kubectl set resources deployment nginx --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi
# Remove the resource requests for resources on containers in nginx
kubectl set resources deployment nginx --limits=cpu=0,memory=0 --requests=cpu=0,memory=0
# Print the result (in yaml format) of updating nginx container limits from a local, without hitting the server
kubectl set resources -f path/to/file.yaml --limits=cpu=200m,memory=512Mi --local -o yaml`)
)
// ResourcesOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags
type ResourcesOptions struct {
resource.FilenameOptions
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Infos []*resource.Info
Encoder runtime.Encoder
Out io.Writer
Err io.Writer
Selector string
ContainerSelector string
ShortOutput bool
All bool
Record bool
ChangeCause string
Local bool
Cmd *cobra.Command
Limits string
Requests string
ResourceRequirements api.ResourceRequirements
PrintObject func(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
Resources []string
}
func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
options := &ResourcesOptions{
Out: out,
Err: errOut,
}
resourceTypesWithPodTemplate := []string{}
for _, resource := range f.SuggestedPodTemplateResources() {
resourceTypesWithPodTemplate = append(resourceTypesWithPodTemplate, resource.Resource)
}
cmd := &cobra.Command{
Use: "resources (-f FILENAME | TYPE NAME) ([--limits=LIMITS & --requests=REQUESTS]",
Short: "update resource requests/limits on objects with pod templates",
Long: fmt.Sprintf(resources_long, strings.Join(resourceTypesWithPodTemplate, ", ")),
Example: resources_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
},
}
cmdutil.AddPrinterFlags(cmd)
//usage := "Filename, directory, or URL to a file identifying the resource to get from the server"
//kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", false, "select all resources in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
cmd.Flags().StringVarP(&options.ContainerSelector, "containers", "c", "*", "The names of containers in the selected pod templates to change, all containers are selected by default - may use wildcards")
cmd.Flags().BoolVar(&options.Local, "local", false, "If true, set resources will NOT contact api-server but run locally.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmd.Flags().StringVar(&options.Limits, "limits", options.Limits, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
cmd.Flags().StringVar(&options.Requests, "requests", options.Requests, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
return cmd
}
func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.Mapper, o.Typer = f.Object()
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.Encoder = f.JSONEncoder()
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command()
o.PrintObject = f.PrintObject
o.Cmd = cmd
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
builder := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
//FilenameParam(enforceNamespace, o.Filenames...).
FilenameParam(enforceNamespace, &o.FilenameOptions).
Flatten()
if !o.Local {
builder = builder.
SelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, args...).
Latest()
}
o.Infos, err = builder.Do().Infos()
if err != nil {
return err
}
return nil
}
func (o *ResourcesOptions) Validate() error {
var err error
if len(o.Limits) == 0 && len(o.Requests) == 0 {
return fmt.Errorf("you must specify an update to requests or limits (in the form of --requests/--limits)")
}
o.ResourceRequirements, err = kubectl.HandleResourceRequirements(map[string]string{"limits": o.Limits, "requests": o.Requests})
if err != nil {
return err
}
return nil
}
func (o *ResourcesOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
transformed := false
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
if len(containers) != 0 {
for i := range containers {
if len(o.Limits) != 0 && len(containers[i].Resources.Limits) == 0 {
containers[i].Resources.Limits = make(api.ResourceList)
}
for key, value := range o.ResourceRequirements.Limits {
containers[i].Resources.Limits[key] = value
}
if len(o.Requests) != 0 && len(containers[i].Resources.Requests) == 0 {
containers[i].Resources.Requests = make(api.ResourceList)
}
for key, value := range o.ResourceRequirements.Requests {
containers[i].Resources.Requests[key] = value
}
transformed = true
}
} else {
allErrs = append(allErrs, fmt.Errorf("error: unable to find container named %s", o.ContainerSelector))
}
return nil
})
if transformed && err == nil {
return runtime.Encode(o.Encoder, info.Object)
}
return nil, err
})
for _, patch := range patches {
info := patch.Info
if patch.Err != nil {
allErrs = append(allErrs, fmt.Errorf("error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err))
continue
}
//no changes
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
allErrs = append(allErrs, fmt.Errorf("info: %s %q was not changed\n", info.Mapping.Resource, info.Name))
continue
}
if o.Local || cmdutil.GetDryRunFlag(o.Cmd) {
return o.PrintObject(o.Cmd, o.Mapper, info.Object, o.Out)
}
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch.Patch)
if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch limit update to pod template %v\n", err))
continue
}
info.Refresh(obj, true)
//record this change (for rollout history)
if o.Record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(obj, o.ChangeCause); err == nil {
if obj, err = resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, obj); err != nil {
allErrs = append(allErrs, fmt.Errorf("changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err))
}
}
}
info.Refresh(obj, true)
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, false, "resource requirements updated")
}
return utilerrors.NewAggregate(allErrs)
}

View file

@ -0,0 +1,226 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"fmt"
"io"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
// SelectorOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type SelectorOptions struct {
fileOptions resource.FilenameOptions
local bool
dryrun bool
all bool
record bool
changeCause string
resources []string
selector *metav1.LabelSelector
out io.Writer
PrintObject func(obj runtime.Object) error
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
builder *resource.Builder
mapper meta.RESTMapper
encoder runtime.Encoder
}
var (
selectorLong = templates.LongDesc(`
Set the selector on a resource. Note that the new selector will overwrite the old selector if the resource had one prior to the invocation
of 'set selector'.
A selector must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.
Note: currently selectors can only be set on Service objects.`)
selectorExample = templates.Examples(`
# set the labels and selector before creating a deployment/service pair.
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
kubectl create deployment my-dep -o yaml --dry-run | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
)
// NewCmdSelector is the "set selector" command.
func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &SelectorOptions{
out: out,
}
cmd := &cobra.Command{
Use: "selector (-f FILENAME | TYPE NAME) EXPRESSIONS [--resource-version=version]",
Short: "Set the selector on a resource",
Long: fmt.Sprintf(selectorLong, validation.LabelValueMaxLength),
Example: selectorExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args, out))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.RunSelector())
},
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().Bool("all", false, "Select all resources in the namespace of the specified resource types")
cmd.Flags().Bool("local", false, "If true, set selector will NOT contact api-server but run locally.")
cmd.Flags().String("resource-version", "", "If non-empty, the selectors update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.")
usage := "the resource to update the selectors"
cmdutil.AddFilenameOptionFlags(cmd, &options.fileOptions, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
return cmd
}
// Complete assigns the SelectorOptions from args.
func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
o.local = cmdutil.GetFlagBool(cmd, "local")
o.all = cmdutil.GetFlagBool(cmd, "all")
o.record = cmdutil.GetRecordFlag(cmd)
o.dryrun = cmdutil.GetDryRunFlag(cmd)
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
o.changeCause = f.Command()
mapper, _ := f.Object()
o.mapper = mapper
o.encoder = f.JSONEncoder()
o.builder = f.NewBuilder().
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.fileOptions).
Flatten()
o.PrintObject = func(obj runtime.Object) error {
return f.PrintObject(cmd, mapper, obj, o.out)
}
o.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.ClientForMapping(mapping)
}
o.resources, o.selector, err = getResourcesAndSelector(args)
return err
}
// Validate basic inputs
func (o *SelectorOptions) Validate() error {
if len(o.resources) < 1 && cmdutil.IsFilenameEmpty(o.fileOptions.Filenames) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
if o.selector == nil {
return fmt.Errorf("one selector is required")
}
return nil
}
// RunSelector executes the command.
func (o *SelectorOptions) RunSelector() error {
if !o.local {
o.builder = o.builder.ResourceTypeOrNameArgs(o.all, o.resources...).
Latest()
}
r := o.builder.Do()
err := r.Err()
if err != nil {
return err
}
return r.Visit(func(info *resource.Info, err error) error {
patch := &Patch{Info: info}
CalculatePatch(patch, o.encoder, func(info *resource.Info) ([]byte, error) {
selectErr := updateSelectorForObject(info.Object, *o.selector)
if selectErr == nil {
return runtime.Encode(o.encoder, info.Object)
}
return nil, selectErr
})
if patch.Err != nil {
return patch.Err
}
if o.local || o.dryrun {
fmt.Fprintln(o.out, "running in local/dry-run mode...")
o.PrintObject(info.Object)
return nil
}
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch.Patch)
if err != nil {
return err
}
if o.record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(patched, o.changeCause); err == nil {
if patched, err = resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, patched); err != nil {
return fmt.Errorf("changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err)
}
}
}
info.Refresh(patched, true)
cmdutil.PrintSuccess(o.mapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, "selector updated")
return nil
})
}
func updateSelectorForObject(obj runtime.Object, selector metav1.LabelSelector) error {
copyOldSelector := func() (map[string]string, error) {
if len(selector.MatchExpressions) > 0 {
return nil, fmt.Errorf("match expression %v not supported on this object", selector.MatchExpressions)
}
dst := make(map[string]string)
for label, value := range selector.MatchLabels {
dst[label] = value
}
return dst, nil
}
var err error
switch t := obj.(type) {
case *api.Service:
t.Spec.Selector, err = copyOldSelector()
default:
err = fmt.Errorf("setting a selector is only supported for Services")
}
return err
}
// getResourcesAndSelector retrieves resources and the selector expression from the given args (assuming selectors the last arg)
func getResourcesAndSelector(args []string) (resources []string, selector *metav1.LabelSelector, err error) {
if len(args) == 0 {
return []string{}, nil, nil
}
resources = args[:len(args)-1]
selector, err = metav1.ParseToLabelSelector(args[len(args)-1])
return resources, selector, err
}

View file

@ -0,0 +1,309 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestUpdateSelectorForObjectTypes(t *testing.T) {
before := metav1.LabelSelector{MatchLabels: map[string]string{"fee": "true"},
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "foo",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"on", "yes"},
},
}}
rc := api.ReplicationController{}
ser := api.Service{}
dep := extensions.Deployment{Spec: extensions.DeploymentSpec{Selector: &before}}
ds := extensions.DaemonSet{Spec: extensions.DaemonSetSpec{Selector: &before}}
rs := extensions.ReplicaSet{Spec: extensions.ReplicaSetSpec{Selector: &before}}
job := batch.Job{Spec: batch.JobSpec{Selector: &before}}
pvc := api.PersistentVolumeClaim{Spec: api.PersistentVolumeClaimSpec{Selector: &before}}
sa := api.ServiceAccount{}
type args struct {
obj runtime.Object
selector metav1.LabelSelector
}
tests := []struct {
name string
args args
wantErr bool
}{
{name: "rc",
args: args{
obj: &rc,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "ser",
args: args{
obj: &ser,
selector: metav1.LabelSelector{},
},
wantErr: false,
},
{name: "dep",
args: args{
obj: &dep,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "ds",
args: args{
obj: &ds,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "rs",
args: args{
obj: &rs,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "job",
args: args{
obj: &job,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "pvc - no updates",
args: args{
obj: &pvc,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
{name: "sa - no selector",
args: args{
obj: &sa,
selector: metav1.LabelSelector{},
},
wantErr: true,
},
}
for _, tt := range tests {
if err := updateSelectorForObject(tt.args.obj, tt.args.selector); (err != nil) != tt.wantErr {
t.Errorf("%q. updateSelectorForObject() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
}
}
func TestUpdateNewSelectorValuesForObject(t *testing.T) {
ser := api.Service{}
type args struct {
obj runtime.Object
selector metav1.LabelSelector
}
tests := []struct {
name string
args args
wantErr bool
}{
{name: "empty",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
wantErr: false,
},
{name: "label-only",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{"b": "u"},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
wantErr: false,
},
}
for _, tt := range tests {
if err := updateSelectorForObject(tt.args.obj, tt.args.selector); (err != nil) != tt.wantErr {
t.Errorf("%q. updateSelectorForObject() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
assert.EqualValues(t, tt.args.selector.MatchLabels, ser.Spec.Selector, tt.name)
}
}
func TestUpdateOldSelectorValuesForObject(t *testing.T) {
ser := api.Service{Spec: api.ServiceSpec{Selector: map[string]string{"fee": "true"}}}
type args struct {
obj runtime.Object
selector metav1.LabelSelector
}
tests := []struct {
name string
args args
wantErr bool
}{
{name: "empty",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
wantErr: false,
},
{name: "label-only",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{"fee": "false", "x": "y"},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
wantErr: false,
},
{name: "expr-only - err",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "a",
Operator: "In",
Values: []string{"x", "y"},
},
},
},
},
wantErr: true,
},
{name: "both - err",
args: args{
obj: &ser,
selector: metav1.LabelSelector{
MatchLabels: map[string]string{"b": "u"},
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "a",
Operator: "In",
Values: []string{"x", "y"},
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
err := updateSelectorForObject(tt.args.obj, tt.args.selector)
if (err != nil) != tt.wantErr {
t.Errorf("%q. updateSelectorForObject() error = %v, wantErr %v", tt.name, err, tt.wantErr)
} else if !tt.wantErr {
assert.EqualValues(t, tt.args.selector.MatchLabels, ser.Spec.Selector, tt.name)
}
}
}
func TestGetResourcesAndSelector(t *testing.T) {
type args struct {
args []string
}
tests := []struct {
name string
args args
wantResources []string
wantSelector *metav1.LabelSelector
wantErr bool
}{
{
name: "basic match",
args: args{args: []string{"rc/foo", "healthy=true"}},
wantResources: []string{"rc/foo"},
wantErr: false,
wantSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"healthy": "true"},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
{
name: "basic expression",
args: args{args: []string{"rc/foo", "buildType notin (debug, test)"}},
wantResources: []string{"rc/foo"},
wantErr: false,
wantSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "buildType",
Operator: "NotIn",
Values: []string{"debug", "test"},
},
},
},
},
{
name: "selector error",
args: args{args: []string{"rc/foo", "buildType notthis (debug, test)"}},
wantResources: []string{"rc/foo"},
wantErr: true,
wantSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
},
{
name: "no resource and selector",
args: args{args: []string{}},
wantResources: []string{},
wantErr: false,
wantSelector: nil,
},
}
for _, tt := range tests {
gotResources, gotSelector, err := getResourcesAndSelector(tt.args.args)
if err != nil {
if !tt.wantErr {
t.Errorf("%q. getResourcesAndSelector() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
continue
}
if !reflect.DeepEqual(gotResources, tt.wantResources) {
t.Errorf("%q. getResourcesAndSelector() gotResources = %v, want %v", tt.name, gotResources, tt.wantResources)
}
if !reflect.DeepEqual(gotSelector, tt.wantSelector) {
t.Errorf("%q. getResourcesAndSelector() gotSelector = %v, want %v", tt.name, gotSelector, tt.wantSelector)
}
}
}

View file

@ -0,0 +1,47 @@
/*
Copyright 2016 The Kubernetes 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 set
import (
"bytes"
"testing"
"github.com/spf13/cobra"
clientcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
func TestLocalAndDryRunFlags(t *testing.T) {
out := &bytes.Buffer{}
errout := &bytes.Buffer{}
f := clientcmdutil.NewFactory(nil)
setCmd := NewCmdSet(f, out, errout)
ensureLocalAndDryRunFlagsOnChildren(t, setCmd, "")
}
func ensureLocalAndDryRunFlagsOnChildren(t *testing.T, c *cobra.Command, prefix string) {
for _, cmd := range c.Commands() {
name := prefix + cmd.Name()
if localFlag := cmd.Flag("local"); localFlag == nil {
t.Errorf("Command %s does not implement the --local flag", name)
}
if dryRunFlag := cmd.Flag("dry-run"); dryRunFlag == nil {
t.Errorf("Command %s does not implement the --dry-run flag", name)
}
ensureLocalAndDryRunFlagsOnChildren(t, cmd, name+".")
}
}