Skip to content

Commit

Permalink
Merge pull request #2127 from ironcladlou/rolling-deploy
Browse files Browse the repository at this point in the history
Merged by openshift-bot
  • Loading branch information
OpenShift Bot committed May 12, 2015
2 parents 34c57a9 + 4ad08f1 commit d69677c
Show file tree
Hide file tree
Showing 26 changed files with 918 additions and 99 deletions.
2 changes: 1 addition & 1 deletion assets/test/e2e/test.js
Expand Up @@ -88,7 +88,7 @@ describe('', function() {
expect(element(by.cssContainingText("h2.service","frontend")).isPresent()).toBe(true);
expect(element(by.cssContainingText(".pod-template-image","Build: ruby-sample-build")).isPresent()).toBe(true);
expect(element(by.cssContainingText(".deployment-trigger","new image for test/origin-ruby-sample:latest")).isPresent()).toBe(true);
expect(element.all(by.css(".pod-running")).count()).toEqual(2);
expect(element.all(by.css(".pod-running")).count()).toEqual(3);
});
});

Expand Down
Binary file added cpu.pprof
Binary file not shown.
4 changes: 2 additions & 2 deletions examples/sample-app/application-template-custombuild.json
Expand Up @@ -165,10 +165,10 @@
"replicaSelector": {
"name": "frontend"
},
"replicas": 1
"replicas": 2
},
"strategy": {
"type": "Recreate"
"type": "Rolling"
}
},
"triggers": [
Expand Down
4 changes: 2 additions & 2 deletions examples/sample-app/application-template-dockerbuild.json
Expand Up @@ -158,10 +158,10 @@
"replicaSelector": {
"name": "frontend"
},
"replicas": 1
"replicas": 2
},
"strategy": {
"type": "Recreate"
"type": "Rolling"
}
},
"triggers": [
Expand Down
36 changes: 2 additions & 34 deletions examples/sample-app/application-template-stibuild.json
Expand Up @@ -158,42 +158,10 @@
"replicaSelector": {
"name": "frontend"
},
"replicas": 1
"replicas": 2
},
"strategy": {
"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"
}
]
}
}
}
"type": "Rolling"
}
},
"triggers": [
Expand Down
21 changes: 19 additions & 2 deletions pkg/api/serialization_test.go
Expand Up @@ -105,9 +105,26 @@ func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, se
},
func(j *deploy.DeploymentStrategy, c fuzz.Continue) {
c.FuzzNoCustom(j)
// TODO: we should not have to set defaults, instead we should be able to detect defaults were applied.
if len(j.Type) == 0 {
mkintp := func(i int) *int64 {
v := int64(i)
return &v
}
switch c.Intn(3) {
case 0:
// TODO: we should not have to set defaults, instead we should be able
// to detect defaults were applied.
j.Type = deploy.DeploymentStrategyTypeRolling
j.RollingParams = &deploy.RollingDeploymentStrategyParams{
IntervalSeconds: mkintp(1),
UpdatePeriodSeconds: mkintp(1),
TimeoutSeconds: mkintp(120),
}
case 1:
j.Type = deploy.DeploymentStrategyTypeRecreate
j.RollingParams = nil
case 2:
j.Type = deploy.DeploymentStrategyTypeCustom
j.RollingParams = nil
}
},
func(j *deploy.DeploymentCauseImageTrigger, c fuzz.Continue) {
Expand Down
39 changes: 26 additions & 13 deletions pkg/cmd/infra/deployer/deployer.go
Expand Up @@ -14,7 +14,9 @@ import (
"github.com/openshift/origin/pkg/cmd/util"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
deployapi "github.com/openshift/origin/pkg/deploy/api"
strategy "github.com/openshift/origin/pkg/deploy/strategy/recreate"
"github.com/openshift/origin/pkg/deploy/strategy"
"github.com/openshift/origin/pkg/deploy/strategy/recreate"
"github.com/openshift/origin/pkg/deploy/strategy/rolling"
deployutil "github.com/openshift/origin/pkg/deploy/util"
"github.com/openshift/origin/pkg/version"
)
Expand Down Expand Up @@ -78,21 +80,35 @@ func NewCommandDeployer(name string) *cobra.Command {

// deploy executes a deployment strategy.
func deploy(kClient kclient.Interface, namespace, deploymentName string) error {
newDeployment, oldDeployments, err := getDeployerContext(&realReplicationControllerGetter{kClient}, namespace, deploymentName)

deployment, oldDeployments, err := getDeployerContext(&realReplicationControllerGetter{kClient}, namespace, deploymentName)
if err != nil {
return err
}

// TODO: Choose a strategy based on some input
strategy := strategy.NewRecreateDeploymentStrategy(kClient, latest.Codec)
return strategy.Deploy(newDeployment, oldDeployments)
config, err := deployutil.DecodeDeploymentConfig(deployment, latest.Codec)
if err != nil {
return fmt.Errorf("couldn't decode DeploymentConfig from deployment %s/%s: %v", deployment.Namespace, deployment.Name, err)
}

var strategy strategy.DeploymentStrategy

switch config.Template.Strategy.Type {
case deployapi.DeploymentStrategyTypeRecreate:
strategy = recreate.NewRecreateDeploymentStrategy(kClient, latest.Codec)
case deployapi.DeploymentStrategyTypeRolling:
recreate := recreate.NewRecreateDeploymentStrategy(kClient, latest.Codec)
strategy = rolling.NewRollingDeploymentStrategy(deployment.Namespace, kClient, latest.Codec, recreate)
default:
return fmt.Errorf("unsupported strategy type: %s", config.Template.Strategy.Type)
}

return strategy.Deploy(deployment, oldDeployments)
}

// getDeployerContext finds the target deployment and any deployments it considers to be prior to the
// target deployment. Only deployments whose LatestVersion is less than the target deployment are
// considered to be prior.
func getDeployerContext(controllerGetter replicationControllerGetter, namespace, deploymentName string) (*kapi.ReplicationController, []kapi.ObjectReference, error) {
func getDeployerContext(controllerGetter replicationControllerGetter, namespace, deploymentName string) (*kapi.ReplicationController, []*kapi.ReplicationController, error) {
var err error
var newDeployment *kapi.ReplicationController
var newConfig *deployapi.DeploymentConfig
Expand All @@ -112,14 +128,14 @@ func getDeployerContext(controllerGetter replicationControllerGetter, namespace,
// encoded DeploymentConfigs to the new one by LatestVersion. Treat a failure to interpret a given
// old deployment as a fatal error to prevent overlapping deployments.
var allControllers *kapi.ReplicationControllerList
oldDeployments := []kapi.ObjectReference{}
oldDeployments := []*kapi.ReplicationController{}

if allControllers, err = controllerGetter.List(newDeployment.Namespace, labels.Everything()); err != nil {
return nil, nil, fmt.Errorf("Unable to get list replication controllers in deployment namespace %s: %v", newDeployment.Namespace, err)
}

glog.Infof("Inspecting %d potential prior deployments", len(allControllers.Items))
for _, controller := range allControllers.Items {
for i, controller := range allControllers.Items {
if configName, hasConfigName := controller.Annotations[deployapi.DeploymentConfigAnnotation]; !hasConfigName {
glog.Infof("Disregarding replicationController %s (not a deployment)", controller.Name)
continue
Expand All @@ -135,10 +151,7 @@ func getDeployerContext(controllerGetter replicationControllerGetter, namespace,

if oldVersion < newConfig.LatestVersion {
glog.Infof("Marking deployment %s as a prior deployment", controller.Name)
oldDeployments = append(oldDeployments, kapi.ObjectReference{
Namespace: controller.Namespace,
Name: controller.Name,
})
oldDeployments = append(oldDeployments, &allControllers.Items[i])
} else {
glog.Infof("Disregarding deployment %s (same as or newer than target)", controller.Name)
}
Expand Down
30 changes: 26 additions & 4 deletions pkg/cmd/infra/deployer/deployer_test.go
Expand Up @@ -96,13 +96,13 @@ func TestGetDeploymentContextNoPriorDeployments(t *testing.T) {
func TestGetDeploymentContextWithPriorDeployments(t *testing.T) {
getter := &testReplicationControllerGetter{
getFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
deployment, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(2), kapi.Codec)
deployment, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(3), kapi.Codec)
return deployment, nil
},
listFunc: func(namespace string, selector labels.Selector) (*kapi.ReplicationControllerList, error) {
deployment1, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(1), kapi.Codec)
deployment2, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(2), kapi.Codec)
deployment3, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(3), kapi.Codec)
deployment3, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(4), kapi.Codec)
deployment4, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(1), kapi.Codec)
deployment4.Annotations[deployapi.DeploymentConfigAnnotation] = "another-config"
return &kapi.ReplicationControllerList{
Expand Down Expand Up @@ -131,8 +131,30 @@ func TestGetDeploymentContextWithPriorDeployments(t *testing.T) {
t.Fatal("expected non-nil oldDeployments")
}

if e, a := 1, len(oldDeployments); e != a {
t.Fatalf("expected oldDeployments with size %d, got %d: %#v", e, a, oldDeployments)
expected := []string{"config-1", "config-2"}
for _, e := range expected {
found := false
for _, d := range oldDeployments {
if d.Name == e {
found = true
break
}
}
if !found {
t.Errorf("expected to find old deployment %s", e)
}
}
for _, d := range oldDeployments {
ok := false
for _, e := range expected {
if d.Name == e {
ok = true
break
}
}
if !ok {
t.Errorf("unexpected old deployment %s", d.Name)
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/cmd/server/origin/master.go
Expand Up @@ -844,10 +844,10 @@ func (c *MasterConfig) RunDeploymentController() error {
env = append(env, clientcmd.EnvVarsFromConfig(c.DeployerClientConfig())...)

factory := deploycontroller.DeploymentControllerFactory{
KubeClient: kclient,
Codec: latest.Codec,
Environment: env,
RecreateStrategyImage: c.ImageFor("deployer"),
KubeClient: kclient,
Codec: latest.Codec,
Environment: env,
DeployerImage: c.ImageFor("deployer"),
}

controller := factory.Create()
Expand Down
18 changes: 18 additions & 0 deletions pkg/deploy/api/types.go
Expand Up @@ -54,6 +54,8 @@ type DeploymentStrategy struct {
CustomParams *CustomDeploymentStrategyParams `json:"customParams,omitempty"`
// RecreateParams are the input to the Recreate deployment strategy.
RecreateParams *RecreateDeploymentStrategyParams `json:"recreateParams,omitempty"`
// RollingParams are the input to the Rolling deployment strategy.
RollingParams *RollingDeploymentStrategyParams `json:"rollingParams,omitempty"`
// Compute resource requirements to execute the deployment
Resources kapi.ResourceRequirements `json:"resources,omitempty"`
}
Expand All @@ -66,6 +68,8 @@ const (
DeploymentStrategyTypeRecreate DeploymentStrategyType = "Recreate"
// DeploymentStrategyTypeCustom is a user defined strategy.
DeploymentStrategyTypeCustom DeploymentStrategyType = "Custom"
// DeploymentStrategyTypeRolling uses the Kubernetes RollingUpdater.
DeploymentStrategyTypeRolling DeploymentStrategyType = "Rolling"
)

// CustomDeploymentStrategyParams are the input to the Custom deployment strategy.
Expand Down Expand Up @@ -123,6 +127,20 @@ type ExecNewPodHook struct {
ContainerName string `json:"containerName"`
}

// RollingDeploymentStrategyParams are the input to the Rolling deployment
// strategy.
type RollingDeploymentStrategyParams struct {
// UpdatePeriodSeconds is the time to wait between individual pod updates.
// If the value is nil, a default will be used.
UpdatePeriodSeconds *int64 `json:"updatePeriodSeconds,omitempty" description:"the time to wait between individual pod updates"`
// IntervalSeconds is the time to wait between polling deployment status
// after update. If the value is nil, a default will be used.
IntervalSeconds *int64 `json:"intervalSeconds,omitempty" description:"the time to wait between polling deployment status after update"`
// TimeoutSeconds is the time to wait for updates before giving up. If the
// value is nil, a default will be used.
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty" description:"the time to wait for updates before giving up"`
}

// DeploymentList is a collection of deployments.
// DEPRECATED: Like Deployment, this is no longer used.
type DeploymentList struct {
Expand Down
6 changes: 6 additions & 0 deletions pkg/deploy/api/v1beta1/conversion.go
Expand Up @@ -19,6 +19,9 @@ func init() {
if err := s.Convert(&in.RecreateParams, &out.RecreateParams, 0); err != nil {
return err
}
if err := s.Convert(&in.RollingParams, &out.RollingParams, 0); err != nil {
return err
}
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
return err
}
Expand All @@ -34,6 +37,9 @@ func init() {
if err := s.Convert(&in.RecreateParams, &out.RecreateParams, 0); err != nil {
return err
}
if err := s.Convert(&in.RollingParams, &out.RollingParams, 0); err != nil {
return err
}
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
return err
}
Expand Down
46 changes: 46 additions & 0 deletions pkg/deploy/api/v1beta1/defaults.go
@@ -0,0 +1,46 @@
package v1beta1

import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"

deployapi "github.com/openshift/origin/pkg/deploy/api"
)

func init() {
mkintp := func(i int) *int64 {
v := int64(i)
return &v
}

err := api.Scheme.AddDefaultingFuncs(
func(obj *deployapi.DeploymentStrategy) {
if len(obj.Type) == 0 {
obj.Type = deployapi.DeploymentStrategyTypeRolling
}

if obj.Type == deployapi.DeploymentStrategyTypeRolling && obj.RollingParams == nil {
obj.RollingParams = &deployapi.RollingDeploymentStrategyParams{
IntervalSeconds: mkintp(1),
UpdatePeriodSeconds: mkintp(1),
TimeoutSeconds: mkintp(120),
}
}
},
func(obj *deployapi.RollingDeploymentStrategyParams) {
if obj.IntervalSeconds == nil {
obj.IntervalSeconds = mkintp(1)
}

if obj.UpdatePeriodSeconds == nil {
obj.UpdatePeriodSeconds = mkintp(1)
}

if obj.TimeoutSeconds == nil {
obj.TimeoutSeconds = mkintp(120)
}
},
)
if err != nil {
panic(err)
}
}

0 comments on commit d69677c

Please sign in to comment.