Skip to content

Add support for 'osc stop|label' from upstream #1944

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

Merged
merged 1 commit into from
May 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ information that's related to a given resource.
$ osc describe service frontend
```

osc label
----------------

This command adds labels to a provided resource.
It can also overwrite the existing labels by using the `--overwrite` flag.

#### Examples

```
$ osc label service frontend foo=bar
```

osc stop
----------------

This command gracefully shuts down a resource by id or filename.
It attempts to shut down and delete a resource that supports graceful termination.
If the resource is resizable, it will be resized to 0 before deletion.

#### Examples

```
$ osc stop service frontend
```

osc namespace
-----------------

Expand Down
12 changes: 12 additions & 0 deletions hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,18 @@ osc resize rc ruby-hello-world-1 --current-replicas=1 --replicas=5
osc delete all -l app=ruby
echo "resize: ok"

osc process -f examples/sample-app/application-template-dockerbuild.json -l app=dockerbuild | osc create -f -
wait_for_command 'osc get rc/database-1' "${TIME_MIN}"
osc get dc/database
osc stop dc/database
[ ! "$(osc get rc/database-1)" ]
[ ! "$(osc get dc/database)" ]
echo "stop: ok"
osc label bc ruby-sample-build acustom=label
[ "$(osc describe bc/ruby-sample-build | grep 'acustom=label')" ]
osc delete all -l app=dockerbuild
echo "label: ok"

osc process -f examples/sample-app/application-template-dockerbuild.json -l build=docker | osc create -f -
osc get buildConfigs
osc get bc
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func NewCommandCLI(name, fullName string) *cobra.Command {
cmds.AddCommand(cmd.NewCmdConfig(fullName, "config"))
cmds.AddCommand(cmd.NewCmdOptions(f, out))
cmds.AddCommand(cmd.NewCmdResize(fullName, f, out))
cmds.AddCommand(cmd.NewCmdStop(fullName, f, out))
cmds.AddCommand(cmd.NewCmdLabel(fullName, f, out))

return cmds
}
Expand Down
76 changes: 67 additions & 9 deletions pkg/cmd/cli/cmd/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import (
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

func tab(original string) string {
lines := []string{}
scanner := bufio.NewScanner(strings.NewReader(original))
for scanner.Scan() {
lines = append(lines, " "+scanner.Text())
}
return strings.Join(lines, "\n")
}

const (
get_long = `Display one or many resources.

Expand Down Expand Up @@ -223,15 +232,6 @@ func NewCmdProxy(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Co
return cmd
}

func tab(original string) string {
lines := []string{}
scanner := bufio.NewScanner(strings.NewReader(original))
for scanner.Scan() {
lines = append(lines, " "+scanner.Text())
}
return strings.Join(lines, "\n")
}

const (
resizeLong = `Set a new size for a Replication Controller either directly or via its Deployment Configuration.

Expand All @@ -253,3 +253,61 @@ func NewCmdResize(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.C
cmd.Example = fmt.Sprintf(resizeExample, fullName)
return cmd
}

const (
stopLong = `Gracefully shut down a resource by id or filename.

Attempts to shut down and delete a resource that supports graceful termination.
If the resource is resizable it will be resized to 0 before deletion.`

stopExample = `// Shut down foo.
$ %[1]s stop replicationcontroller foo

// Stop pods and services with label name=myLabel.
$ %[1]s stop pods,services -l name=myLabel

// Shut down the service defined in service.json
$ %[1]s stop -f service.json

// Shut down all resources in the path/to/resources directory
$ %[1]s stop -f path/to/resources`
)

// NewCmdStop is a wrapper for the Kubernetes cli stop command
func NewCmdStop(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
cmd := kcmd.NewCmdStop(f.Factory, out)
cmd.Long = stopLong
cmd.Example = fmt.Sprintf(stopExample, fullName)
return cmd
}

const (
labelLong = `Update the labels on a resource.

If --overwrite is true, then existing labels can be overwritten, otherwise attempting to overwrite a label will result in an error.
If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.`

labelExample = `// Update pod 'foo' with the label 'unhealthy' and the value 'true'.
$ %[1]s label pods foo unhealthy=true

// Update pod 'foo' with the label 'status' and the value 'unhealthy', overwriting any existing value.
$ %[1]s label --overwrite pods foo status=unhealthy

// Update all pods in the namespace
$ %[1]s label pods --all status=unhealthy

// Update pod 'foo' only if the resource is unchanged from version 1.
$ %[1]s 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.
$ %[1]s label pods foo bar-`
)

// NewCmdLabel is a wrapper for the Kubernetes cli label command
func NewCmdLabel(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
cmd := kcmd.NewCmdLabel(f.Factory, out)
cmd.Long = labelLong
cmd.Example = fmt.Sprintf(labelExample, fullName)
return cmd
}
12 changes: 9 additions & 3 deletions pkg/cmd/util/clientcmd/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/spf13/pflag"

"github.com/openshift/origin/pkg/api/latest"
"github.com/openshift/origin/pkg/client"
"github.com/openshift/origin/pkg/cmd/cli/describe"
deployapi "github.com/openshift/origin/pkg/deploy/api"
deployreaper "github.com/openshift/origin/pkg/deploy/reaper"
deployres "github.com/openshift/origin/pkg/deploy/resizer"

"github.com/spf13/pflag"
)

// NewFactory creates a default Factory for commands that should share identical server
Expand Down Expand Up @@ -109,7 +109,13 @@ func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory {
}
return deployres.ResizerFor(mapping.Kind, osc, kc)
}

w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
osc, kc, err := w.Clients()
if err != nil {
return nil, err
}
return deployreaper.ReaperFor(mapping.Kind, osc, kc)
}
w.Printer = func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return describe.NewHumanReadablePrinter(noHeaders), nil
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/deploy/reaper/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package reaper implements the Reaper interface for deploymentConfigs
package reaper
68 changes: 68 additions & 0 deletions pkg/deploy/reaper/reaper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package reaper

import (
"fmt"
"time"

kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/golang/glog"

"github.com/openshift/origin/pkg/client"
deployres "github.com/openshift/origin/pkg/deploy/resizer"
"github.com/openshift/origin/pkg/deploy/util"
)

const (
shortInterval = time.Millisecond * 100
interval = time.Second * 3
timeout = time.Minute * 5
)

// ReaperFor returns the appropriate Reaper client depending on the provided
// kind of resource (Replication controllers, pods, services, and deploymentConfigs
// supported)
func ReaperFor(kind string, osc *client.Client, kc *kclient.Client) (kubectl.Reaper, error) {
if kind != "DeploymentConfig" {
return kubectl.ReaperFor(kind, kc)
}
return &DeploymentConfigReaper{osc: osc, kc: kc, pollInterval: interval, timeout: timeout}, nil
}

// DeploymentConfigReaper implements the Reaper interface for deploymentConfigs
type DeploymentConfigReaper struct {
osc client.Interface
kc kclient.Interface
pollInterval, timeout time.Duration
}

// Stop resizes a replication controller via its deployment configuration down to
// zero replicas, waits for all of them to get deleted and then deletes both the
// replication controller and its deployment configuration.
func (reaper *DeploymentConfigReaper) Stop(namespace, name string, gracePeriod *kapi.DeleteOptions) (string, error) {
resizer, err := deployres.ResizerFor("DeploymentConfig", reaper.osc.(*client.Client), reaper.kc.(*kclient.Client))
if err != nil {
return "", err
}
retry := &kubectl.RetryParams{Interval: shortInterval, Timeout: reaper.timeout}
waitForReplicas := &kubectl.RetryParams{reaper.pollInterval, reaper.timeout}
if err = resizer.Resize(namespace, name, 0, nil, retry, waitForReplicas); err != nil {
// The deploymentConfig may not have a replication controller to resize
// so we shouldn't error out here
glog.V(2).Info(err)
}
dc, err := reaper.osc.DeploymentConfigs(namespace).Get(name)
if err != nil {
return "", err
}
if err = reaper.kc.ReplicationControllers(namespace).Delete(util.LatestDeploymentNameForConfig(dc)); err != nil {
// The deploymentConfig may not have a replication controller to delete
// so we shouldn't error out here
glog.V(2).Info(err)
}
if err = reaper.osc.DeploymentConfigs(namespace).Delete(name); err != nil {
return "", err
}
return fmt.Sprintf("%s stopped", name), nil
}
2 changes: 1 addition & 1 deletion pkg/deploy/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func LabelForDeploymentConfig(config *deployapi.DeploymentConfig) string {
return fmt.Sprintf("%s/%s:%d", config.Namespace, config.Name, config.LatestVersion)
}

// HashPodSpecs hashes a PodSpec into a uint64.
// HashPodSpec hashes a PodSpec into a uint64.
// TODO: Resources are currently ignored due to the formats not surviving encoding/decoding
// in a consistent manner (e.g. 0 is represented sometimes as 0.000)
func HashPodSpec(t api.PodSpec) uint64 {
Expand Down