Skip to content
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
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