2015-02-07 00:33:42 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2015-02-07 00:33:42 +00:00
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 (
"fmt"
"io"
"strings"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
2015-02-07 00:33:42 +00:00
"github.com/spf13/cobra"
)
2015-02-20 21:28:43 +00:00
const (
label_long = ` Update the labels on a resource .
2015-02-07 00:33:42 +00:00
2015-05-20 09:02:30 +00:00
A label must begin with a letter or number , and may contain letters , numbers , hyphens , dots , and underscores , up to % [ 1 ] d characters .
2015-02-07 00:33:42 +00:00
If -- overwrite is true , then existing labels can be overwritten , otherwise attempting to overwrite a label will result in an error .
2015-02-20 21:28:43 +00:00
If -- resource - version is specified , then updates will use this resource version , otherwise the existing resource - version will be used . `
label_example = ` // Update pod 'foo' with the label 'unhealthy' and the value 'true'.
$ kubectl label pods foo unhealthy = true
2015-02-07 00:33:42 +00:00
2015-02-20 21:28:43 +00:00
// Update pod 'foo' with the label 'status' and the value 'unhealthy', overwriting any existing value.
$ kubectl label -- overwrite pods foo status = unhealthy
2015-02-07 00:33:42 +00:00
2015-03-26 01:52:12 +00:00
// Update all pods in the namespace
$ kubectl label pods -- all status = unhealthy
2015-02-20 21:28:43 +00:00
// Update pod 'foo' only if the resource is unchanged from version 1.
$ kubectl label pods foo status = unhealthy -- resource - version = 1
// Update pod 'foo' by removing a label named 'bar' if it exists.
// Does not require the --overwrite flag.
$ kubectl label pods foo bar - `
)
2015-04-07 18:21:25 +00:00
func NewCmdLabel ( f * cmdutil . Factory , out io . Writer ) * cobra . Command {
2015-02-20 21:28:43 +00:00
cmd := & cobra . Command {
2015-07-06 11:31:27 +00:00
Use : "label [--overwrite] TYPE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]" ,
2015-02-20 21:28:43 +00:00
Short : "Update the labels on a resource" ,
2015-05-20 09:02:30 +00:00
Long : fmt . Sprintf ( label_long , util . LabelValueMaxLength ) ,
2015-02-20 21:28:43 +00:00
Example : label_example ,
2015-02-07 00:33:42 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2015-03-09 22:08:16 +00:00
err := RunLabel ( f , out , cmd , args )
2015-04-07 18:21:25 +00:00
cmdutil . CheckErr ( err )
2015-02-07 00:33:42 +00:00
} ,
}
2015-04-07 18:21:25 +00:00
cmdutil . AddPrinterFlags ( cmd )
2015-02-07 00:33:42 +00:00
cmd . Flags ( ) . Bool ( "overwrite" , false , "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels." )
2015-03-26 01:52:12 +00:00
cmd . Flags ( ) . StringP ( "selector" , "l" , "" , "Selector (label query) to filter on" )
cmd . Flags ( ) . Bool ( "all" , false , "select all resources in the namespace of the specified resource types" )
cmd . Flags ( ) . String ( "resource-version" , "" , "If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource." )
2015-02-07 00:33:42 +00:00
return cmd
}
func validateNoOverwrites ( meta * api . ObjectMeta , labels map [ string ] string ) error {
for key := range labels {
if value , found := meta . Labels [ key ] ; found {
return fmt . Errorf ( "'%s' already has a value (%s), and --overwrite is false" , key , value )
}
}
return nil
}
func parseLabels ( spec [ ] string ) ( map [ string ] string , [ ] string , error ) {
labels := map [ string ] string { }
var remove [ ] string
for _ , labelSpec := range spec {
if strings . Index ( labelSpec , "=" ) != - 1 {
parts := strings . Split ( labelSpec , "=" )
2015-05-20 09:02:30 +00:00
if len ( parts ) != 2 || len ( parts [ 1 ] ) == 0 || ! util . IsValidLabelValue ( parts [ 1 ] ) {
2015-02-07 00:33:42 +00:00
return nil , nil , fmt . Errorf ( "invalid label spec: %v" , labelSpec )
}
labels [ parts [ 0 ] ] = parts [ 1 ]
} else if strings . HasSuffix ( labelSpec , "-" ) {
remove = append ( remove , labelSpec [ : len ( labelSpec ) - 1 ] )
} else {
2015-03-31 22:32:02 +00:00
return nil , nil , fmt . Errorf ( "unknown label spec: %v" , labelSpec )
2015-02-07 00:33:42 +00:00
}
}
for _ , removeLabel := range remove {
if _ , found := labels [ removeLabel ] ; found {
return nil , nil , fmt . Errorf ( "can not both modify and remove a label in the same command" )
}
}
return labels , remove , nil
}
2015-07-23 22:43:48 +00:00
func labelFunc ( obj runtime . Object , overwrite bool , resourceVersion string , labels map [ string ] string , remove [ ] string ) error {
2015-02-07 00:33:42 +00:00
meta , err := api . ObjectMetaFor ( obj )
if err != nil {
2015-07-23 22:43:48 +00:00
return err
2015-02-07 00:33:42 +00:00
}
if ! overwrite {
if err := validateNoOverwrites ( meta , labels ) ; err != nil {
2015-07-23 22:43:48 +00:00
return err
2015-02-07 00:33:42 +00:00
}
}
2015-02-11 11:05:41 +00:00
if meta . Labels == nil {
meta . Labels = make ( map [ string ] string )
}
2015-02-07 00:33:42 +00:00
for key , value := range labels {
meta . Labels [ key ] = value
}
for _ , label := range remove {
delete ( meta . Labels , label )
}
if len ( resourceVersion ) != 0 {
meta . ResourceVersion = resourceVersion
}
2015-07-23 22:43:48 +00:00
return nil
2015-02-07 00:33:42 +00:00
}
2015-03-09 22:08:16 +00:00
2015-04-07 18:21:25 +00:00
func RunLabel ( f * cmdutil . Factory , out io . Writer , cmd * cobra . Command , args [ ] string ) error {
2015-03-26 01:52:12 +00:00
resources , labelArgs := [ ] string { } , [ ] string { }
first := true
for _ , s := range args {
isLabel := strings . Contains ( s , "=" ) || strings . HasSuffix ( s , "-" )
switch {
case first && isLabel :
first = false
fallthrough
case ! first && isLabel :
labelArgs = append ( labelArgs , s )
case first && ! isLabel :
resources = append ( resources , s )
case ! first && ! isLabel :
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "all resources must be specified before label changes: %s" , s )
2015-03-26 01:52:12 +00:00
}
2015-03-09 22:08:16 +00:00
}
2015-03-26 01:52:12 +00:00
if len ( resources ) < 1 {
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "one or more resources must be specified as <resource> <name> or <resource>/<name>" )
2015-03-09 22:08:16 +00:00
}
2015-03-26 01:52:12 +00:00
if len ( labelArgs ) < 1 {
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "at least one label update is required" )
2015-03-09 22:08:16 +00:00
}
2015-04-07 18:21:25 +00:00
selector := cmdutil . GetFlagString ( cmd , "selector" )
all := cmdutil . GetFlagBool ( cmd , "all" )
overwrite := cmdutil . GetFlagBool ( cmd , "overwrite" )
resourceVersion := cmdutil . GetFlagString ( cmd , "resource-version" )
2015-03-26 01:52:12 +00:00
2015-06-26 20:49:34 +00:00
cmdNamespace , _ , err := f . DefaultNamespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2015-03-26 01:52:12 +00:00
labels , remove , err := parseLabels ( labelArgs )
2015-03-09 22:08:16 +00:00
if err != nil {
2015-05-20 09:02:30 +00:00
return cmdutil . UsageError ( cmd , err . Error ( ) )
2015-03-09 22:08:16 +00:00
}
2015-03-26 01:52:12 +00:00
mapper , typer := f . Object ( )
2015-03-27 13:46:17 +00:00
b := resource . NewBuilder ( mapper , typer , f . ClientMapperForCommand ( ) ) .
2015-03-26 01:52:12 +00:00
ContinueOnError ( ) .
NamespaceParam ( cmdNamespace ) . DefaultNamespace ( ) .
SelectorParam ( selector ) .
ResourceTypeOrNameArgs ( all , resources ... ) .
Flatten ( ) .
Latest ( )
one := false
r := b . Do ( ) . IntoSingular ( & one )
if err := r . Err ( ) ; err != nil {
2015-03-09 22:08:16 +00:00
return err
}
2015-03-26 01:52:12 +00:00
// only apply resource version locking on a single resource
if ! one && len ( resourceVersion ) > 0 {
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "--resource-version may only be used with a single resource" )
2015-03-26 01:52:12 +00:00
}
2015-03-09 22:08:16 +00:00
2015-03-26 01:52:12 +00:00
// TODO: support bulk generic output a la Get
return r . Visit ( func ( info * resource . Info ) error {
2015-07-23 22:43:48 +00:00
obj , err := cmdutil . UpdateObject ( info , func ( obj runtime . Object ) error {
err := labelFunc ( obj , overwrite , resourceVersion , labels , remove )
2015-03-26 01:52:12 +00:00
if err != nil {
2015-07-23 22:43:48 +00:00
return err
2015-03-26 01:52:12 +00:00
}
2015-07-23 22:43:48 +00:00
return nil
2015-03-26 01:52:12 +00:00
} )
2015-03-09 22:08:16 +00:00
if err != nil {
2015-03-26 01:52:12 +00:00
return err
2015-03-09 22:08:16 +00:00
}
2015-05-12 11:14:31 +00:00
printer , err := f . PrinterForMapping ( cmd , info . Mapping , false )
2015-03-26 01:52:12 +00:00
if err != nil {
return err
}
return printer . PrintObj ( obj , out )
} )
2015-03-09 22:08:16 +00:00
}