2016-11-14 11:53:27 +00:00
/ *
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"
"github.com/spf13/cobra"
2018-11-09 18:49:10 +00:00
"k8s.io/klog"
2018-05-21 19:27:11 +00:00
2017-11-13 06:27:59 +00:00
"k8s.io/api/core/v1"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
2017-01-16 20:13:59 +00:00
"k8s.io/apimachinery/pkg/types"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/validation"
2018-08-21 10:46:39 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
2016-11-14 11:53:27 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-05-14 14:51:51 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 18:29:30 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2016-11-14 11:53:27 +00:00
)
2018-10-30 10:35:24 +00:00
// SetSelectorOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
2016-11-14 11:53:27 +00:00
// referencing the cmd.Flags()
2018-04-19 12:32:15 +00:00
type SetSelectorOptions struct {
2018-05-29 15:18:50 +00:00
// Bound
ResourceBuilderFlags * genericclioptions . ResourceBuilderFlags
PrintFlags * genericclioptions . PrintFlags
RecordFlags * genericclioptions . RecordFlags
dryrun bool
2016-11-14 11:53:27 +00:00
2018-05-29 15:18:50 +00:00
// set by args
2016-11-14 11:53:27 +00:00
resources [ ] string
selector * metav1 . LabelSelector
2018-05-29 15:18:50 +00:00
// computed
WriteToServer bool
PrintObj printers . ResourcePrinterFunc
Recorder genericclioptions . Recorder
ResourceFinder genericclioptions . ResourceFinder
2018-04-25 12:32:08 +00:00
2018-05-29 15:18:50 +00:00
// set at initialization
2018-04-25 12:32:08 +00:00
genericclioptions . IOStreams
2016-11-14 11:53:27 +00:00
}
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 .
2017-01-16 07:55:30 +00:00
kubectl create service clusterip my - svc -- clusterip = "None" - o yaml -- dry - run | kubectl set selector -- local - f - ' environment = qa ' - o yaml | kubectl create - f -
2016-11-14 11:53:27 +00:00
kubectl create deployment my - dep - o yaml -- dry - run | kubectl label -- local - f - environment = qa - o yaml | kubectl create - f - ` )
)
2018-10-30 10:35:24 +00:00
// NewSelectorOptions returns an initialized SelectorOptions instance
2018-04-25 12:32:08 +00:00
func NewSelectorOptions ( streams genericclioptions . IOStreams ) * SetSelectorOptions {
2018-04-19 12:32:15 +00:00
return & SetSelectorOptions {
2018-05-29 15:18:50 +00:00
ResourceBuilderFlags : genericclioptions . NewResourceBuilderFlags ( ) .
WithScheme ( scheme . Scheme ) .
WithAll ( false ) .
WithLocal ( false ) .
WithUninitialized ( false ) .
WithLatest ( ) ,
2018-05-02 19:15:47 +00:00
PrintFlags : genericclioptions . NewPrintFlags ( "selector updated" ) . WithTypeSetter ( scheme . Scheme ) ,
2018-04-19 12:32:15 +00:00
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
Recorder : genericclioptions . NoopRecorder { } ,
2018-04-04 20:23:33 +00:00
2018-04-25 12:32:08 +00:00
IOStreams : streams ,
2016-11-14 11:53:27 +00:00
}
2018-04-19 12:32:15 +00:00
}
// NewCmdSelector is the "set selector" command.
2018-04-25 12:32:08 +00:00
func NewCmdSelector ( f cmdutil . Factory , streams genericclioptions . IOStreams ) * cobra . Command {
o := NewSelectorOptions ( streams )
2016-11-14 11:53:27 +00:00
cmd := & cobra . Command {
2018-10-05 19:59:38 +00:00
Use : "selector (-f FILENAME | TYPE NAME) EXPRESSIONS [--resource-version=version]" ,
2017-10-11 06:26:02 +00:00
DisableFlagsInUseLine : true ,
2018-10-05 19:59:38 +00:00
Short : i18n . T ( "Set the selector on a resource" ) ,
Long : fmt . Sprintf ( selectorLong , validation . LabelValueMaxLength ) ,
Example : selectorExample ,
2016-11-14 11:53:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-04-19 12:32:15 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( ) )
cmdutil . CheckErr ( o . RunSelector ( ) )
2016-11-14 11:53:27 +00:00
} ,
}
2018-04-04 20:23:33 +00:00
2018-05-29 15:18:50 +00:00
o . ResourceBuilderFlags . AddFlags ( cmd . Flags ( ) )
2018-04-19 12:32:15 +00:00
o . PrintFlags . AddFlags ( cmd )
o . RecordFlags . AddFlags ( cmd )
2018-04-04 20:23:33 +00:00
2016-11-14 11:53:27 +00:00
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." )
cmdutil . AddDryRunFlag ( cmd )
return cmd
}
// Complete assigns the SelectorOptions from args.
2018-04-19 12:32:15 +00:00
func ( o * SetSelectorOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
var err error
2018-05-21 19:27:11 +00:00
o . RecordFlags . Complete ( cmd )
2018-04-19 12:32:15 +00:00
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
2016-11-14 11:53:27 +00:00
o . dryrun = cmdutil . GetDryRunFlag ( cmd )
2017-06-19 08:25:06 +00:00
o . resources , o . selector , err = getResourcesAndSelector ( args )
if err != nil {
return err
}
2016-11-14 11:53:27 +00:00
2018-05-29 15:18:50 +00:00
o . ResourceFinder = o . ResourceBuilderFlags . ToBuilder ( f , o . resources )
o . WriteToServer = ! ( * o . ResourceBuilderFlags . Local || o . dryrun )
2017-05-16 21:52:51 +00:00
2018-04-06 15:36:52 +00:00
if o . dryrun {
o . PrintFlags . Complete ( "%s (dry run)" )
}
2018-04-04 20:23:33 +00:00
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
2018-04-12 20:02:30 +00:00
o . PrintObj = printer . PrintObj
2018-04-06 15:36:52 +00:00
2016-11-14 11:53:27 +00:00
return err
}
// Validate basic inputs
2018-04-19 12:32:15 +00:00
func ( o * SetSelectorOptions ) Validate ( ) error {
2016-12-29 04:47:24 +00:00
if o . selector == nil {
return fmt . Errorf ( "one selector is required" )
}
2016-11-14 11:53:27 +00:00
return nil
}
// RunSelector executes the command.
2018-04-19 12:32:15 +00:00
func ( o * SetSelectorOptions ) RunSelector ( ) error {
2018-05-29 15:18:50 +00:00
r := o . ResourceFinder . Do ( )
2016-11-14 11:53:27 +00:00
return r . Visit ( func ( info * resource . Info , err error ) error {
patch := & Patch { Info : info }
2018-05-21 21:56:56 +00:00
CalculatePatch ( patch , scheme . DefaultJSONEncoder ( ) , func ( obj runtime . Object ) ( [ ] byte , error ) {
2017-11-14 04:01:51 +00:00
selectErr := updateSelectorForObject ( info . Object , * o . selector )
2018-04-19 12:32:15 +00:00
if selectErr != nil {
return nil , selectErr
}
2016-11-14 11:53:27 +00:00
2018-04-19 12:32:15 +00:00
// record this change (for rollout history)
if err := o . Recorder . Record ( patch . Info . Object ) ; err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
2016-11-14 11:53:27 +00:00
}
2018-04-19 12:32:15 +00:00
2018-05-14 14:51:51 +00:00
return runtime . Encode ( scheme . DefaultJSONEncoder ( ) , info . Object )
2016-11-14 11:53:27 +00:00
} )
if patch . Err != nil {
return patch . Err
}
2018-05-29 15:18:50 +00:00
if ! o . WriteToServer {
2018-04-25 12:32:08 +00:00
return o . PrintObj ( info . Object , o . Out )
2016-11-14 11:53:27 +00:00
}
2018-08-30 13:33:34 +00:00
actual , err := resource . NewHelper ( info . Client , info . Mapping ) . Patch ( info . Namespace , info . Name , types . StrategicMergePatchType , patch . Patch , nil )
2016-11-14 11:53:27 +00:00
if err != nil {
return err
}
2018-05-14 14:51:51 +00:00
return o . PrintObj ( actual , o . Out )
2016-11-14 11:53:27 +00:00
} )
}
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 ) {
2017-11-13 06:27:59 +00:00
case * v1 . Service :
2016-11-14 11:53:27 +00:00
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 ) {
2016-12-29 04:47:24 +00:00
if len ( args ) == 0 {
return [ ] string { } , nil , nil
2016-11-14 11:53:27 +00:00
}
2016-12-29 04:47:24 +00:00
resources = args [ : len ( args ) - 1 ]
2016-11-14 11:53:27 +00:00
selector , err = metav1 . ParseToLabelSelector ( args [ len ( args ) - 1 ] )
return resources , selector , err
}