Skip to content

Commit

Permalink
Merge pull request #1645 from ironcladlou/deployment-hooks-2-electric…
Browse files Browse the repository at this point in the history
…-boogaloo

Merged by openshift-bot
  • Loading branch information
OpenShift Bot committed Apr 14, 2015
2 parents 0a11617 + 1edbe52 commit 438add4
Show file tree
Hide file tree
Showing 12 changed files with 1,226 additions and 39 deletions.
34 changes: 33 additions & 1 deletion examples/sample-app/application-template-stibuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,39 @@
"replicas": 1
},
"strategy": {
"type": "Recreate"
"type": "Recreate",
"recreateParams": {
"pre": {
"failurePolicy": "Abort",
"execNewPod": {
"containerName": "ruby-helloworld",
"command": [
"/bin/true"
],
"env": [
{
"name": "CUSTOM_VAR1",
"value": "custom_value1"
}
]
}
},
"post": {
"failurePolicy": "Ignore",
"execNewPod": {
"containerName": "ruby-helloworld",
"command": [
"/bin/false"
],
"env": [
{
"name": "CUSTOM_VAR2",
"value": "custom_value2"
}
]
}
}
}
}
},
"triggers": [
Expand Down
59 changes: 36 additions & 23 deletions pkg/cmd/cli/describe/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, er
formatString(out, "Latest Version", strconv.Itoa(deploymentConfig.LatestVersion))
}

printStrategy(deploymentConfig.Template.Strategy, out)
printTriggers(deploymentConfig.Triggers, out)

formatString(out, "Strategy", deploymentConfig.Template.Strategy.Type)
printStrategy(deploymentConfig.Template.Strategy, out)
printReplicationControllerSpec(deploymentConfig.Template.ControllerTemplate, out)

deploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig)
Expand All @@ -113,53 +115,64 @@ func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, er
})
}

func printStrategy(strategy deployapi.DeploymentStrategy, w io.Writer) {
fmt.Fprintf(w, "Strategy:\t%s\n", strategy.Type)
func printStrategy(strategy deployapi.DeploymentStrategy, w *tabwriter.Writer) {
switch strategy.Type {
case deployapi.DeploymentStrategyTypeRecreate:
if strategy.RecreateParams != nil {
pre := strategy.RecreateParams.Pre
post := strategy.RecreateParams.Post
if pre != nil {
printHook("Pre-deployment", pre, w)
}
if post != nil {
printHook("Post-deployment", post, w)
}
}
case deployapi.DeploymentStrategyTypeCustom:
fmt.Fprintf(w, "\t- Image:\t%s\n", strategy.CustomParams.Image)
fmt.Fprintf(w, "\t Image:\t%s\n", strategy.CustomParams.Image)

if len(strategy.CustomParams.Environment) > 0 {
fmt.Fprintf(w, "\t- Environment:\t%s\n", formatLabels(convertEnv(strategy.CustomParams.Environment)))
fmt.Fprintf(w, "\t Environment:\t%s\n", formatLabels(convertEnv(strategy.CustomParams.Environment)))
}

if len(strategy.CustomParams.Command) > 0 {
fmt.Fprintf(w, "\t- Command:\t%v\n", strings.Join(strategy.CustomParams.Command, " "))
fmt.Fprintf(w, "\t Command:\t%v\n", strings.Join(strategy.CustomParams.Command, " "))
}
}
}

func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w io.Writer) {
func printHook(prefix string, hook *deployapi.LifecycleHook, w io.Writer) {
if hook.ExecNewPod != nil {
fmt.Fprintf(w, "\t %s hook (pod type, failure policy: %s)\n", prefix, hook.FailurePolicy)
fmt.Fprintf(w, "\t Container:\t%s\n", hook.ExecNewPod.ContainerName)
fmt.Fprintf(w, "\t Command:\t%v\n", strings.Join(hook.ExecNewPod.Command, " "))
fmt.Fprintf(w, "\t Env:\t%s\n", formatLabels(convertEnv(hook.ExecNewPod.Env)))
}
}

func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w *tabwriter.Writer) {
if len(triggers) == 0 {
fmt.Fprint(w, "Triggers:\t<none>\n")
formatString(w, "Triggers", "<none>")
return
}

fmt.Fprint(w, "Triggers:\n")
labels := []string{}

for _, t := range triggers {
fmt.Fprintf(w, "\t- %s\n", t.Type)
switch t.Type {
case deployapi.DeploymentTriggerOnConfigChange:
fmt.Fprintf(w, "\t\t<no options>\n")
labels = append(labels, "Config")
case deployapi.DeploymentTriggerOnImageChange:
if len(t.ImageChangeParams.RepositoryName) > 0 {
fmt.Fprintf(w, "\t\tAutomatic:\t%v\n\t\tRepository:\t%s\n\t\tTag:\t%s\n",
t.ImageChangeParams.Automatic,
t.ImageChangeParams.RepositoryName,
t.ImageChangeParams.Tag,
)
labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.RepositoryName, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
} else if len(t.ImageChangeParams.From.Name) > 0 {
fmt.Fprintf(w, "\t\tAutomatic:\t%v\n\t\tImage Repository:\t%s\n\t\tTag:\t%s\n",
t.ImageChangeParams.Automatic,
t.ImageChangeParams.From.Name,
t.ImageChangeParams.Tag,
)
labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.From.Name, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
}
default:
fmt.Fprint(w, "unknown\n")
}
}

desc := strings.Join(labels, ", ")
formatString(w, "Triggers", desc)
}

func printReplicationControllerSpec(spec kapi.ReplicationControllerSpec, w io.Writer) error {
Expand Down
31 changes: 31 additions & 0 deletions pkg/cmd/cli/describe/describer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,37 @@ func TestDeploymentConfigDescriber(t *testing.T) {
config.Triggers[0].ImageChangeParams.RepositoryName = ""
config.Triggers[0].ImageChangeParams.From = kapi.ObjectReference{Name: "imageRepo"}
describe()

config.Template.Strategy = deployapitest.OkStrategy()
config.Template.Strategy.RecreateParams = &deployapi.RecreateDeploymentStrategyParams{
Pre: &deployapi.LifecycleHook{
FailurePolicy: deployapi.LifecycleHookFailurePolicyAbort,
ExecNewPod: &deployapi.ExecNewPodHook{
ContainerName: "container",
Command: []string{"/command1", "args"},
Env: []kapi.EnvVar{
{
Name: "KEY1",
Value: "value1",
},
},
},
},
Post: &deployapi.LifecycleHook{
FailurePolicy: deployapi.LifecycleHookFailurePolicyIgnore,
ExecNewPod: &deployapi.ExecNewPodHook{
ContainerName: "container",
Command: []string{"/command2", "args"},
Env: []kapi.EnvVar{
{
Name: "KEY2",
Value: "value2",
},
},
},
},
}
describe()
}

func mkPod(status kapi.PodPhase, exitCode int) *kapi.Pod {
Expand Down
47 changes: 47 additions & 0 deletions pkg/deploy/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type DeploymentStrategy struct {
Type DeploymentStrategyType `json:"type,omitempty"`
// CustomParams are the input to the Custom deployment strategy.
CustomParams *CustomDeploymentStrategyParams `json:"customParams,omitempty"`
// RecreateParams are the input to the Recreate deployment strategy.
RecreateParams *RecreateDeploymentStrategyParams `json:"recreateParams,omitempty"`
}

// DeploymentStrategyType refers to a specific DeploymentStrategy implementation.
Expand All @@ -74,6 +76,51 @@ type CustomDeploymentStrategyParams struct {
Command []string `json:"command,omitempty"`
}

// RecreateDeploymentStrategyParams are the input to the Recreate deployment
// strategy.
type RecreateDeploymentStrategyParams struct {
// Pre is a lifecycle hook which is executed before the strategy manipulates
// the deployment. All LifecycleHookFailurePolicy values are supported.
Pre *LifecycleHook `json:"pre,omitempty"`
// Post is a lifecycle hook which is executed after the strategy has
// finished all deployment logic. The LifecycleHookFailurePolicyAbort policy
// is NOT supported.
Post *LifecycleHook `json:"post,omitempty"`
}

// Handler defines a specific deployment lifecycle action.
type LifecycleHook struct {
// FailurePolicy specifies what action to take if the hook fails.
FailurePolicy LifecycleHookFailurePolicy `json:"failurePolicy"`
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
ExecNewPod *ExecNewPodHook `json:"execNewPod,omitempty"`
}

// HandlerFailurePolicy describes possibles actions to take if a hook fails.
type LifecycleHookFailurePolicy string

const (
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
// LifecycleHookFailurePolicyAbort means abort the deployment (if possible).
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
)

// ExecNewPodHook is a hook implementation which runs a command in a new pod
// based on the specified container which is assumed to be part of the
// deployment template.
type ExecNewPodHook struct {
// Command is the action command and its arguments.
Command []string `json:"command"`
// Env is a set of environment variables to supply to the hook pod's container.
Env []kapi.EnvVar `json:"env,omitempty"`
// ContainerName is the name of a container in the deployment pod template
// whose Docker image will be used for the hook pod's container.
ContainerName string `json:"containerName"`
}

// A DeploymentList is a collection of deployments.
// DEPRECATED: Like Deployment, this is no longer used.
type DeploymentList struct {
Expand Down
47 changes: 47 additions & 0 deletions pkg/deploy/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type DeploymentStrategy struct {
Type DeploymentStrategyType `json:"type,omitempty"`
// CustomParams are the input to the Custom deployment strategy.
CustomParams *CustomDeploymentStrategyParams `json:"customParams,omitempty"`
// RecreateParams are the input to the Recreate deployment strategy.
RecreateParams *RecreateDeploymentStrategyParams `json:"recreateParams,omitempty"`
}

// DeploymentStrategyType refers to a specific DeploymentStrategy implementation.
Expand All @@ -75,6 +77,51 @@ type CustomDeploymentStrategyParams struct {
Command []string `json:"command,omitempty"`
}

// RecreateDeploymentStrategyParams are the input to the Recreate deployment
// strategy.
type RecreateDeploymentStrategyParams struct {
// Pre is a lifecycle hook which is executed before the strategy manipulates
// the deployment. All LifecycleHookFailurePolicy values are supported.
Pre *LifecycleHook `json:"pre,omitempty"`
// Post is a lifecycle hook which is executed after the strategy has
// finished all deployment logic. The LifecycleHookFailurePolicyAbort policy
// is NOT supported.
Post *LifecycleHook `json:"post,omitempty"`
}

// Handler defines a specific deployment lifecycle action.
type LifecycleHook struct {
// FailurePolicy specifies what action to take if the hook fails.
FailurePolicy LifecycleHookFailurePolicy `json:"failurePolicy"`
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
ExecNewPod *ExecNewPodHook `json:"execNewPod,omitempty"`
}

// HandlerFailurePolicy describes possibles actions to take if a hook fails.
type LifecycleHookFailurePolicy string

const (
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
// LifecycleHookFailurePolicyAbort means abort the deployment (if possible).
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
)

// ExecNewPodHook is a hook implementation which runs a command in a new pod
// based on the specified container which is assumed to be part of the
// deployment template.
type ExecNewPodHook struct {
// Command is the action command and its arguments.
Command []string `json:"command"`
// Env is a set of environment variables to supply to the hook pod's container.
Env []kapi.EnvVar `json:"env,omitempty"`
// ContainerName is the name of a container in the deployment pod template
// whose Docker image will be used for the hook pod's container.
ContainerName string `json:"containerName"`
}

// A DeploymentList is a collection of deployments.
// DEPRECATED: Like Deployment, this is no longer used.
type DeploymentList struct {
Expand Down
68 changes: 68 additions & 0 deletions pkg/deploy/api/validation/validation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validation

import (
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
Expand Down Expand Up @@ -77,6 +78,10 @@ func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy) fielderr
}

switch strategy.Type {
case deployapi.DeploymentStrategyTypeRecreate:
if strategy.RecreateParams != nil {
errs = append(errs, validateRecreateParams(strategy.RecreateParams).Prefix("recreateParams")...)
}
case deployapi.DeploymentStrategyTypeCustom:
if strategy.CustomParams == nil {
errs = append(errs, fielderrors.NewFieldRequired("customParams"))
Expand All @@ -98,6 +103,69 @@ func validateCustomParams(params *deployapi.CustomDeploymentStrategyParams) fiel
return errs
}

func validateRecreateParams(params *deployapi.RecreateDeploymentStrategyParams) fielderrors.ValidationErrorList {
errs := fielderrors.ValidationErrorList{}

if params.Pre != nil {
errs = append(errs, validateLifecycleHook(params.Pre).Prefix("pre")...)
}
if params.Post != nil {
errs = append(errs, validateLifecycleHook(params.Post).Prefix("post")...)
}

return errs
}

func validateLifecycleHook(hook *deployapi.LifecycleHook) fielderrors.ValidationErrorList {
errs := fielderrors.ValidationErrorList{}

if len(hook.FailurePolicy) == 0 {
errs = append(errs, fielderrors.NewFieldRequired("failurePolicy"))
}

if hook.ExecNewPod == nil {
errs = append(errs, fielderrors.NewFieldRequired("execNewPod"))
} else {
errs = append(errs, validateExecNewPod(hook.ExecNewPod).Prefix("execNewPod")...)
}

return errs
}

func validateExecNewPod(hook *deployapi.ExecNewPodHook) fielderrors.ValidationErrorList {
errs := fielderrors.ValidationErrorList{}

if len(hook.Command) == 0 {
errs = append(errs, fielderrors.NewFieldRequired("command"))
}

if len(hook.ContainerName) == 0 {
errs = append(errs, fielderrors.NewFieldRequired("containerName"))
}

if len(hook.Env) > 0 {
errs = append(errs, validateEnv(hook.Env).Prefix("env")...)
}

return errs
}

func validateEnv(vars []kapi.EnvVar) fielderrors.ValidationErrorList {
allErrs := fielderrors.ValidationErrorList{}

for i, ev := range vars {
vErrs := fielderrors.ValidationErrorList{}
if len(ev.Name) == 0 {
vErrs = append(vErrs, fielderrors.NewFieldRequired("name"))
}
if !util.IsCIdentifier(ev.Name) {
vErrs = append(vErrs, fielderrors.NewFieldInvalid("name", ev.Name, "must match regex "+util.CIdentifierFmt))
}
allErrs = append(allErrs, vErrs.PrefixIndex(i)...)
}
return allErrs
}

func validateTrigger(trigger *deployapi.DeploymentTriggerPolicy) fielderrors.ValidationErrorList {
errs := fielderrors.ValidationErrorList{}

Expand Down
Loading

0 comments on commit 438add4

Please sign in to comment.