Skip to content

Commit 08aeda9

Browse files
Enable custom deployments to have rolling/recreate as well
Allow customParams to be specified when type is Recreate or Rolling. Allow image to be empty, add validation for resources and environment. Add a custom deployment extended test.
1 parent 400ffd1 commit 08aeda9

File tree

9 files changed

+137
-36
lines changed

9 files changed

+137
-36
lines changed

pkg/deploy/api/types.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ type DeploymentStrategy struct {
2929
// Type is the name of a deployment strategy.
3030
Type DeploymentStrategyType
3131

32-
// CustomParams are the input to the Custom deployment strategy.
33-
CustomParams *CustomDeploymentStrategyParams
3432
// RecreateParams are the input to the Recreate deployment strategy.
3533
RecreateParams *RecreateDeploymentStrategyParams
3634
// RollingParams are the input to the Rolling deployment strategy.
3735
RollingParams *RollingDeploymentStrategyParams
3836

37+
// CustomParams are the input to the Custom deployment strategy, and may also
38+
// be specified for the Recreate and Rolling strategies to customize the execution
39+
// process that runs the deployment.
40+
CustomParams *CustomDeploymentStrategyParams
41+
3942
// Resources contains resource requirements to execute the deployment
4043
Resources kapi.ResourceRequirements
4144
// Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
@@ -50,7 +53,7 @@ type DeploymentStrategyType string
5053
const (
5154
// DeploymentStrategyTypeRecreate is a simple strategy suitable as a default.
5255
DeploymentStrategyTypeRecreate DeploymentStrategyType = "Recreate"
53-
// DeploymentStrategyTypeCustom is a user defined strategy.
56+
// DeploymentStrategyTypeCustom is a user defined strategy. It is optional to set.
5457
DeploymentStrategyTypeCustom DeploymentStrategyType = "Custom"
5558
// DeploymentStrategyTypeRolling uses the Kubernetes RollingUpdater.
5659
DeploymentStrategyTypeRolling DeploymentStrategyType = "Rolling"

pkg/deploy/api/validation/validation.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy, pod *kap
8686
errs = append(errs, field.Required(fldPath.Child("type"), ""))
8787
}
8888

89+
if strategy.CustomParams != nil {
90+
errs = append(errs, validateCustomParams(strategy.CustomParams, fldPath.Child("customParams"))...)
91+
}
92+
8993
switch strategy.Type {
9094
case deployapi.DeploymentStrategyTypeRecreate:
9195
if strategy.RecreateParams != nil {
@@ -100,8 +104,12 @@ func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy, pod *kap
100104
case deployapi.DeploymentStrategyTypeCustom:
101105
if strategy.CustomParams == nil {
102106
errs = append(errs, field.Required(fldPath.Child("customParams"), ""))
103-
} else {
104-
errs = append(errs, validateCustomParams(strategy.CustomParams, fldPath.Child("customParams"))...)
107+
}
108+
if strategy.RollingParams != nil {
109+
errs = append(errs, validateRollingParams(strategy.RollingParams, pod, fldPath.Child("rollingParams"))...)
110+
}
111+
if strategy.RecreateParams != nil {
112+
errs = append(errs, validateRecreateParams(strategy.RecreateParams, pod, fldPath.Child("recreateParams"))...)
105113
}
106114
}
107115

@@ -112,17 +120,15 @@ func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy, pod *kap
112120
errs = append(errs, validation.ValidateAnnotations(strategy.Annotations, fldPath.Child("annotations"))...)
113121
}
114122

115-
// TODO: validate resource requirements (prereq: https://github.com/kubernetes/kubernetes/pull/7059)
123+
errs = append(errs, validation.ValidateResourceRequirements(&strategy.Resources, fldPath.Child("resources"))...)
116124

117125
return errs
118126
}
119127

120128
func validateCustomParams(params *deployapi.CustomDeploymentStrategyParams, fldPath *field.Path) field.ErrorList {
121129
errs := field.ErrorList{}
122130

123-
if len(params.Image) == 0 {
124-
errs = append(errs, field.Required(fldPath.Child("image"), ""))
125-
}
131+
errs = append(errs, validateEnv(params.Environment, fldPath.Child("environment"))...)
126132

127133
return errs
128134
}
@@ -207,7 +213,7 @@ func validateEnv(vars []kapi.EnvVar, fldPath *field.Path) field.ErrorList {
207213

208214
for i, ev := range vars {
209215
vErrs := field.ErrorList{}
210-
idxPath := fldPath.Child("name").Index(i)
216+
idxPath := fldPath.Index(i).Child("name")
211217
if len(ev.Name) == 0 {
212218
vErrs = append(vErrs, field.Required(idxPath, ""))
213219
}

pkg/deploy/api/validation/validation_test.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -240,22 +240,26 @@ func TestValidateDeploymentConfigMissingFields(t *testing.T) {
240240
field.ErrorTypeRequired,
241241
"spec.strategy.customParams",
242242
},
243-
"missing spec.strategy.customParams.image": {
243+
"invalid spec.strategy.customParams.environment": {
244244
api.DeploymentConfig{
245245
ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "bar"},
246246
Spec: api.DeploymentConfigSpec{
247247
Replicas: 1,
248248
Triggers: manualTrigger(),
249249
Selector: test.OkSelector(),
250250
Strategy: api.DeploymentStrategy{
251-
Type: api.DeploymentStrategyTypeCustom,
252-
CustomParams: &api.CustomDeploymentStrategyParams{},
251+
Type: api.DeploymentStrategyTypeCustom,
252+
CustomParams: &api.CustomDeploymentStrategyParams{
253+
Environment: []kapi.EnvVar{
254+
{Name: "A=B"},
255+
},
256+
},
253257
},
254258
Template: test.OkPodTemplate(),
255259
},
256260
},
257-
field.ErrorTypeRequired,
258-
"spec.strategy.customParams.image",
261+
field.ErrorTypeInvalid,
262+
"spec.strategy.customParams.environment[0].name",
259263
},
260264
"missing spec.strategy.recreateParams.pre.failurePolicy": {
261265
api.DeploymentConfig{

pkg/deploy/controller/deployment/controller.go

+2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ func (c *DeploymentController) makeDeployerPod(deployment *kapi.ReplicationContr
265265
},
266266
},
267267
ActiveDeadlineSeconds: &maxDeploymentDurationSeconds,
268+
DNSPolicy: deployment.Spec.Template.Spec.DNSPolicy,
269+
ImagePullSecrets: deployment.Spec.Template.Spec.ImagePullSecrets,
268270
// Setting the node selector on the deployer pod so that it is created
269271
// on the same set of nodes as the pods.
270272
NodeSelector: deployment.Spec.Template.Spec.NodeSelector,

pkg/deploy/controller/deployment/factory.go

+34-19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"k8s.io/kubernetes/pkg/runtime"
1313
"k8s.io/kubernetes/pkg/util/flowcontrol"
1414
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
15+
"k8s.io/kubernetes/pkg/util/sets"
1516
"k8s.io/kubernetes/pkg/watch"
1617

1718
controller "github.com/openshift/origin/pkg/controller"
@@ -128,36 +129,50 @@ func (factory *DeploymentControllerFactory) Create() controller.RunnableControll
128129
// 1. For the Recreate and Rolling strategies, strategy, use the factory's
129130
// DeployerImage as the container image, and the factory's Environment
130131
// as the container environment.
131-
// 2. For all Custom strategy, use the strategy's image for the container
132-
// image, and use the combination of the factory's Environment and the
133-
// strategy's environment as the container environment.
132+
// 2. For all Custom strategies, or if the CustomParams field is set, use
133+
// the strategy's image for the container image, and use the combination
134+
// of the factory's Environment and the strategy's environment as the
135+
// container environment.
134136
//
135137
// An error is returned if the deployment strategy type is not supported.
136138
func (factory *DeploymentControllerFactory) makeContainer(strategy *deployapi.DeploymentStrategy) (*kapi.Container, error) {
139+
image := factory.DeployerImage
140+
var environment []kapi.EnvVar
141+
var command []string
142+
143+
set := sets.NewString()
144+
// Use user-defined values from the strategy input.
145+
if p := strategy.CustomParams; p != nil {
146+
if len(p.Image) > 0 {
147+
image = p.Image
148+
}
149+
if len(p.Command) > 0 {
150+
command = p.Command
151+
}
152+
for _, env := range strategy.CustomParams.Environment {
153+
set.Insert(env.Name)
154+
environment = append(environment, env)
155+
}
156+
}
157+
137158
// Set default environment values
138-
environment := []kapi.EnvVar{}
139159
for _, env := range factory.Environment {
160+
if set.Has(env.Name) {
161+
continue
162+
}
140163
environment = append(environment, env)
141164
}
142165

143166
// Every strategy type should be handled here.
144167
switch strategy.Type {
145-
case deployapi.DeploymentStrategyTypeRecreate, deployapi.DeploymentStrategyTypeRolling:
146-
// Use the factory-configured image.
147-
return &kapi.Container{
148-
Image: factory.DeployerImage,
149-
Env: environment,
150-
}, nil
151-
case deployapi.DeploymentStrategyTypeCustom:
152-
// Use user-defined values from the strategy input.
153-
for _, env := range strategy.CustomParams.Environment {
154-
environment = append(environment, env)
155-
}
156-
return &kapi.Container{
157-
Image: strategy.CustomParams.Image,
158-
Env: environment,
159-
}, nil
168+
case deployapi.DeploymentStrategyTypeRecreate, deployapi.DeploymentStrategyTypeRolling, deployapi.DeploymentStrategyTypeCustom:
160169
default:
161170
return nil, fmt.Errorf("unsupported deployment strategy type: %s", strategy.Type)
162171
}
172+
173+
return &kapi.Container{
174+
Image: image,
175+
Command: command,
176+
Env: environment,
177+
}, nil
163178
}

pkg/deploy/strategy/support/lifecycle.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (e *HookExecutor) executeExecNewPod(hook *deployapi.LifecycleHook, deployme
160160

161161
// Track whether the pod has already run to completion and avoid showing logs
162162
// or the Success message twice.
163-
completed := false
163+
completed, created := false, false
164164

165165
// Try to create the pod.
166166
pod, err := e.podClient.CreatePod(deployment.Namespace, podSpec)
@@ -172,6 +172,7 @@ func (e *HookExecutor) executeExecNewPod(hook *deployapi.LifecycleHook, deployme
172172
pod = podSpec
173173
pod.Namespace = deployment.Namespace
174174
} else {
175+
created = true
175176
fmt.Fprintf(e.out, "--> %s: Running hook pod ...\n", label)
176177
}
177178

@@ -200,7 +201,9 @@ waitLoop:
200201
wg.Done()
201202
break waitLoop
202203
}
203-
fmt.Fprintf(e.out, "--> %s: Hook pod is already running ...\n", label)
204+
if !created {
205+
fmt.Fprintf(e.out, "--> %s: Hook pod is already running ...\n", label)
206+
}
204207
go once.Do(func() { e.readPodLogs(pod, wg) })
205208
break waitLoop
206209
default:

test/extended/deployments/deployments.go

+24
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var _ = g.Describe("deploymentconfigs", func() {
2828
var (
2929
deploymentFixture = exutil.FixturePath("..", "extended", "fixtures", "test-deployment-test.yaml")
3030
simpleDeploymentFixture = exutil.FixturePath("..", "extended", "fixtures", "deployment-simple.yaml")
31+
customDeploymentFixture = exutil.FixturePath("..", "extended", "fixtures", "custom-deployment.yaml")
3132
oc = exutil.NewCLI("cli-deployment", exutil.KubeConfigPath())
3233
)
3334

@@ -185,6 +186,29 @@ var _ = g.Describe("deploymentconfigs", func() {
185186
}
186187
})
187188
})
189+
190+
g.Describe("with custom deployments", func() {
191+
g.It("should run the custom deployment steps [Conformance]", func() {
192+
out, err := oc.Run("create").Args("-f", customDeploymentFixture).Output()
193+
o.Expect(err).NotTo(o.HaveOccurred())
194+
195+
o.Expect(waitForLatestCondition(oc, "custom-deployment", deploymentRunTimeout, deploymentRunning)).NotTo(o.HaveOccurred())
196+
197+
out, err = oc.Run("logs").Args("-f", "dc/custom-deployment").Output()
198+
o.Expect(err).NotTo(o.HaveOccurred())
199+
g.By(fmt.Sprintf("checking the logs for substrings\n%s", out))
200+
o.Expect(out).To(o.ContainSubstring("--> pre: Running hook pod ..."))
201+
o.Expect(out).To(o.ContainSubstring("test pre hook executed"))
202+
o.Expect(out).To(o.ContainSubstring("--> Scaling custom-deployment-1 to 2"))
203+
o.Expect(out).To(o.ContainSubstring("--> Reached 50%"))
204+
o.Expect(out).To(o.ContainSubstring("Halfway"))
205+
o.Expect(out).To(o.ContainSubstring("Finished"))
206+
o.Expect(out).To(o.ContainSubstring("--> Success"))
207+
208+
g.By("verifying the deployment is marked complete")
209+
o.Expect(waitForLatestCondition(oc, "custom-deployment", deploymentRunTimeout, deploymentReachedCompletion)).NotTo(o.HaveOccurred())
210+
})
211+
})
188212
})
189213

190214
func deploymentStatuses(rcs []kapi.ReplicationController) []string {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
apiVersion: v1
2+
kind: DeploymentConfig
3+
metadata:
4+
name: custom-deployment
5+
spec:
6+
replicas: 2
7+
selector:
8+
name: custom-deployment
9+
strategy:
10+
type: Rolling
11+
rollingParams:
12+
pre:
13+
failurePolicy: Abort
14+
execNewPod:
15+
containerName: myapp
16+
command:
17+
- /bin/echo
18+
- test pre hook executed
19+
customParams:
20+
command:
21+
- /bin/sh
22+
- -c
23+
- |
24+
set -e
25+
openshift-deploy --until=50%
26+
echo Halfway
27+
openshift-deploy
28+
echo Finished
29+
template:
30+
metadata:
31+
labels:
32+
name: custom-deployment
33+
spec:
34+
terminationGracePeriodSeconds: 0
35+
containers:
36+
- image: "docker.io/centos:centos7"
37+
imagePullPolicy: IfNotPresent
38+
name: myapp
39+
command:
40+
- /bin/sleep
41+
- "100"
42+
triggers:
43+
- type: ConfigChange

test/extended/fixtures/test-deployment-test.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ spec:
2121
labels:
2222
name: deployment-test
2323
spec:
24+
terminationGracePeriodSeconds: 0
2425
containers:
2526
- image: "docker.io/centos:centos7"
2627
imagePullPolicy: IfNotPresent

0 commit comments

Comments
 (0)