Adds support for multiple resources to kubectl

You can specify multiple resources by name when using the delete, get
and stop commands.
pull/6/head
Karl Beecher 2015-02-19 15:17:23 +01:00
parent 0e1c0b26dc
commit ecbb91cc08
2 changed files with 66 additions and 31 deletions

View File

@ -153,6 +153,15 @@ for version in "${kube_api_versions[@]}"; do
kubectl get pods "${kube_flags[@]}" -lname=redis-master | grep -q 'redis-master'
[ ! $(delete pods --all pods -l name=redis-master) ] # not --all and label selector together
kubectl delete --all pods "${kube_flags[@]}" # --all remove all the pods
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}"
kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}"
kubectl get pods redis-master redis-proxy "${kube_flags[@]}"
kubectl delete pods redis-master redis-proxy # delete multiple pods at once
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ]
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}"
kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}"
kubectl stop pods redis-master redis-proxy # stop multiple pods at once
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ]
@ -185,6 +194,12 @@ __EOF__
kubectl get services "${kube_flags[@]}"
kubectl get services "service-${version}-test" "${kube_flags[@]}"
kubectl delete service frontend "${kube_flags[@]}"
servicesbefore="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")"
kubectl create -f examples/guestbook/frontend-service.json "${kube_flags[@]}"
kubectl create -f examples/guestbook/redis-slave-service.json "${kube_flags[@]}"
kubectl delete services frontend redisslave # delete multiple services at once
servicesafter="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$((${servicesafter} - ${servicesbefore}))" -eq 0 ]
kube::log::status "Testing kubectl(${version}:replicationcontrollers)"
kubectl get replicationcontrollers "${kube_flags[@]}"
@ -192,6 +207,12 @@ __EOF__
kubectl get replicationcontrollers "${kube_flags[@]}"
kubectl describe replicationcontroller frontend-controller "${kube_flags[@]}" | grep -q 'Replicas:.*3 desired'
kubectl delete rc frontend-controller "${kube_flags[@]}"
rcsbefore="$(kubectl get replicationcontrollers -o template -t "{{ len .items }}" "${kube_flags[@]}")"
kubectl create -f examples/guestbook/frontend-controller.json "${kube_flags[@]}"
kubectl create -f examples/guestbook/redis-slave-controller.json "${kube_flags[@]}"
kubectl delete rc frontend-controller redis-slave-controller "${kube_flags[@]}" # delete multiple controllers at once
rcsafter="$(kubectl get replicationcontrollers -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$((${rcsafter} - ${rcsbefore}))" -eq 0 ]
kube::log::status "Testing kubectl(${version}:nodes)"
kubectl get nodes "${kube_flags[@]}"

View File

@ -48,7 +48,7 @@ type Builder struct {
resources []string
namespace string
name string
names []string
defaultNamespace bool
requireNamespace bool
@ -217,22 +217,25 @@ func (b *Builder) SelectAllParam(selectAll bool) *Builder {
return b
}
// ResourceTypeOrNameArgs indicates that the builder should accept one or two arguments
// of the form `(<type1>[,<type2>,...]|<type> <name>)`. When one argument is received, the types
// provided will be retrieved from the server (and be comma delimited). When two arguments are
// received, they must be a single type and name. If more than two arguments are provided an
// error is set. The allowEmptySelector permits to select all the resources (via Everything func).
// ResourceTypeOrNameArgs indicates that the builder should accept arguments
// of the form `(<type1>[,<type2>,...]|<type> <name1>[,<name2>,...])`. When one argument is
// received, the types provided will be retrieved from the server (and be comma delimited).
// When two or more arguments are received, they must be a single type and resource name(s).
// The allowEmptySelector permits to select all the resources (via Everything func).
func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder {
switch len(args) {
case 2:
b.name = args[1]
switch {
case len(args) > 2:
b.names = append(b.names, args[1:]...)
b.ResourceTypes(SplitResourceArgument(args[0])...)
case 1:
case len(args) == 2:
b.names = append(b.names, args[1])
b.ResourceTypes(SplitResourceArgument(args[0])...)
case len(args) == 1:
b.ResourceTypes(SplitResourceArgument(args[0])...)
if b.selector == nil && allowEmptySelector {
b.selector = labels.Everything()
}
case 0:
case len(args) == 0:
default:
b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource or resource and name"))
}
@ -244,7 +247,7 @@ func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string
func (b *Builder) ResourceTypeAndNameArgs(args ...string) *Builder {
switch len(args) {
case 2:
b.name = args[1]
b.names = append(b.names, args[1])
b.ResourceTypes(SplitResourceArgument(args[0])...)
case 0:
default:
@ -312,7 +315,7 @@ func (b *Builder) visitorResult() *Result {
// visit selectors
if b.selector != nil {
if len(b.name) != 0 {
if len(b.names) != 0 {
return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
}
if len(b.resources) == 0 {
@ -349,38 +352,49 @@ func (b *Builder) visitorResult() *Result {
return &Result{visitor: VisitorList(visitors), sources: visitors}
}
// visit single item specified by name
if len(b.name) != 0 {
// visit items specified by name
if len(b.names) != 0 {
isSingular := len(b.names) == 1
if len(b.paths) != 0 {
return &Result{singular: true, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) == 0 {
return &Result{singular: true, err: fmt.Errorf("you must provide a resource and a resource name together")}
return &Result{singular: isSingular, err: fmt.Errorf("you must provide a resource and a resource name together")}
}
if len(b.resources) > 1 {
return &Result{singular: true, err: fmt.Errorf("you must specify only one resource")}
return &Result{singular: isSingular, err: fmt.Errorf("you must specify only one resource")}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{singular: true, err: err}
return &Result{singular: isSingular, err: err}
}
mapping := mappings[0]
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
b.namespace = ""
} else {
if len(b.namespace) == 0 {
return &Result{singular: true, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
}
}
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{singular: true, err: err}
return &Result{err: err}
}
info := NewInfo(client, mappings[0], b.namespace, b.name)
if err := info.Get(); err != nil {
return &Result{singular: true, err: err}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
} else {
if len(b.namespace) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
}
}
return &Result{singular: true, visitor: info, sources: []Visitor{info}}
visitors := []Visitor{}
for _, name := range b.names {
info := NewInfo(client, mapping, selectorNamespace, name)
if err := info.Get(); err != nil {
return &Result{singular: isSingular, err: err}
}
visitors = append(visitors, info)
}
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
}
// visit items specified by paths