Browse code

Correlate and retrieve deployments by label

Use a label to correlate deployments with their deploymentConfig.
This allows efficient query of the deployments for a config without
performance degradation as a factor of increasing non-deployment
controller count in the project.

Dan Mace authored on 2015/06/12 00:06:31
Showing 7 changed files
... ...
@@ -145,13 +145,11 @@ func (o DeployOptions) RunDeploy() error {
145 145
 			return o.kubeClient.ReplicationControllers(namespace).Get(name)
146 146
 		},
147 147
 		ListDeploymentsForConfigFn: func(namespace, configName string) (*kapi.ReplicationControllerList, error) {
148
-			rcs, err := o.kubeClient.ReplicationControllers(namespace).List(labels.Everything())
148
+			list, err := o.kubeClient.ReplicationControllers(namespace).List(deployutil.ConfigSelector(configName))
149 149
 			if err != nil {
150 150
 				return nil, err
151 151
 			}
152
-			return &kapi.ReplicationControllerList{
153
-				Items: deployutil.ConfigSelector(configName, rcs.Items),
154
-			}, nil
152
+			return list, nil
155 153
 		},
156 154
 
157 155
 		UpdateDeploymentConfigFn: func(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9 9
 	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
12 11
 	"github.com/golang/glog"
13 12
 	"github.com/spf13/cobra"
14 13
 
... ...
@@ -83,8 +82,8 @@ func NewDeployer(client kclient.Interface) *Deployer {
83 83
 		getDeployment: func(namespace, name string) (*kapi.ReplicationController, error) {
84 84
 			return client.ReplicationControllers(namespace).Get(name)
85 85
 		},
86
-		getControllers: func(namespace string) (*kapi.ReplicationControllerList, error) {
87
-			return client.ReplicationControllers(namespace).List(labels.Everything())
86
+		getDeployments: func(namespace, configName string) (*kapi.ReplicationControllerList, error) {
87
+			return client.ReplicationControllers(namespace).List(deployutil.ConfigSelector(configName))
88 88
 		},
89 89
 		scaler: scaler,
90 90
 		strategyFor: func(config *deployapi.DeploymentConfig) (strategy.DeploymentStrategy, error) {
... ...
@@ -114,8 +113,8 @@ type Deployer struct {
114 114
 	strategyFor func(config *deployapi.DeploymentConfig) (strategy.DeploymentStrategy, error)
115 115
 	// getDeployment finds the named deployment.
116 116
 	getDeployment func(namespace, name string) (*kapi.ReplicationController, error)
117
-	// getControllers finds all controllers in namespace.
118
-	getControllers func(namespace string) (*kapi.ReplicationControllerList, error)
117
+	// getDeployments finds all deployments associated with a config.
118
+	getDeployments func(namespace, configName string) (*kapi.ReplicationControllerList, error)
119 119
 	// scaler is used to scale replication controllers.
120 120
 	scaler kubectl.Scaler
121 121
 }
... ...
@@ -146,14 +145,14 @@ func (d *Deployer) Deploy(namespace, deploymentName string) error {
146 146
 		return fmt.Errorf("deployment %s has no desired replica count", deployutil.LabelForDeployment(deployment))
147 147
 	}
148 148
 
149
-	// Find all controllers in order to pick out the deployments.
150
-	controllers, err := d.getControllers(namespace)
149
+	// Find all deployments for the config.
150
+	unsortedDeployments, err := d.getDeployments(namespace, config.Name)
151 151
 	if err != nil {
152 152
 		return fmt.Errorf("couldn't get controllers in namespace %s: %v", namespace, err)
153 153
 	}
154
+	deployments := unsortedDeployments.Items
154 155
 
155
-	// Find all deployments sorted by version.
156
-	deployments := deployutil.ConfigSelector(config.Name, controllers.Items)
156
+	// Sort all the deployments by version.
157 157
 	sort.Sort(deployutil.DeploymentsByLatestVersionDesc(deployments))
158 158
 
159 159
 	// Find any last completed deployment.
... ...
@@ -23,7 +23,7 @@ func TestDeployer_getDeploymentFail(t *testing.T) {
23 23
 		getDeployment: func(namespace, name string) (*kapi.ReplicationController, error) {
24 24
 			return nil, fmt.Errorf("get error")
25 25
 		},
26
-		getControllers: func(namespace string) (*kapi.ReplicationControllerList, error) {
26
+		getDeployments: func(namespace, configName string) (*kapi.ReplicationControllerList, error) {
27 27
 			t.Fatal("unexpected call")
28 28
 			return nil, nil
29 29
 		},
... ...
@@ -144,7 +144,7 @@ func TestDeployer_deployScenarios(t *testing.T) {
144 144
 			getDeployment: func(namespace, name string) (*kapi.ReplicationController, error) {
145 145
 				return to, nil
146 146
 			},
147
-			getControllers: func(namespace string) (*kapi.ReplicationControllerList, error) {
147
+			getDeployments: func(namespace, configName string) (*kapi.ReplicationControllerList, error) {
148 148
 				list := &kapi.ReplicationControllerList{}
149 149
 				for _, d := range s.deployments {
150 150
 					list.Items = append(list.Items, *d)
... ...
@@ -52,12 +52,12 @@ func (factory *DeploymentConfigControllerFactory) Create() controller.RunnableCo
52 52
 				return factory.KubeClient.ReplicationControllers(namespace).Create(deployment)
53 53
 			},
54 54
 			listDeploymentsForConfigFunc: func(namespace, configName string) (*kapi.ReplicationControllerList, error) {
55
-				rcList, err := factory.KubeClient.ReplicationControllers(namespace).List(labels.Everything())
55
+				sel := deployutil.ConfigSelector(configName)
56
+				list, err := factory.KubeClient.ReplicationControllers(namespace).List(sel)
56 57
 				if err != nil {
57 58
 					return nil, err
58 59
 				}
59
-				rcList.Items = deployutil.ConfigSelector(configName, rcList.Items)
60
-				return rcList, nil
60
+				return list, nil
61 61
 			},
62 62
 			updateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
63 63
 				return factory.KubeClient.ReplicationControllers(namespace).Update(deployment)
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
9 9
 	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
12 11
 	"github.com/golang/glog"
13 12
 
14 13
 	"github.com/openshift/origin/pkg/client"
... ...
@@ -52,7 +51,7 @@ func (reaper *DeploymentConfigReaper) Stop(namespace, name string, gracePeriod *
52 52
 	}
53 53
 
54 54
 	// Clean up deployments related to the config.
55
-	rcList, err := reaper.kc.ReplicationControllers(namespace).List(labels.Everything())
55
+	rcList, err := reaper.kc.ReplicationControllers(namespace).List(util.ConfigSelector(name))
56 56
 	if err != nil {
57 57
 		return "", err
58 58
 	}
... ...
@@ -62,7 +61,7 @@ func (reaper *DeploymentConfigReaper) Stop(namespace, name string, gracePeriod *
62 62
 	}
63 63
 
64 64
 	// If there is neither a config nor any deployments, we can return NotFound.
65
-	deployments := util.ConfigSelector(name, rcList.Items)
65
+	deployments := rcList.Items
66 66
 	if configNotFound && len(deployments) == 0 {
67 67
 		return "", kerrors.NewNotFound("DeploymentConfig", name)
68 68
 	}
... ...
@@ -109,15 +109,13 @@ func LabelForDeploymentConfig(config *deployapi.DeploymentConfig) string {
109 109
 	return fmt.Sprintf("%s/%s:%d", config.Namespace, config.Name, config.LatestVersion)
110 110
 }
111 111
 
112
-// ConfigSelector matches all the deployments of the provided DeploymentConfig
113
-func ConfigSelector(name string, list []api.ReplicationController) []api.ReplicationController {
114
-	matches := []api.ReplicationController{}
115
-	for _, rc := range list {
116
-		if DeploymentConfigNameFor(&rc) == name {
117
-			matches = append(matches, rc)
118
-		}
119
-	}
120
-	return matches
112
+// ConfigSelector returns a label Selector which can be used to find all
113
+// deployments for a DeploymentConfig.
114
+//
115
+// TODO: Using the annotation constant for now since the value is correct
116
+// but we could consider adding a new constant to the public types.
117
+func ConfigSelector(name string) labels.Selector {
118
+	return labels.Set{deployapi.DeploymentConfigAnnotation: name}.AsSelector()
121 119
 }
122 120
 
123 121
 // DecodeDeploymentConfig decodes a DeploymentConfig from controller using codec. An error is returned
... ...
@@ -165,6 +163,10 @@ func MakeDeployment(config *deployapi.DeploymentConfig, codec runtime.Codec) (*a
165 165
 	for k, v := range config.Labels {
166 166
 		controllerLabels[k] = v
167 167
 	}
168
+	// Correlate the deployment with the config.
169
+	// TODO: Using the annotation constant for now since the value is correct
170
+	// but we could consider adding a new constant to the public types.
171
+	controllerLabels[deployapi.DeploymentConfigAnnotation] = config.Name
168 172
 
169 173
 	// Ensure that pods created by this deployment controller can be safely associated back
170 174
 	// to the controller, and that multiple deployment controllers for the same config don't
... ...
@@ -107,6 +107,10 @@ func TestMakeDeploymentOk(t *testing.T) {
107 107
 		t.Fatalf("expected deployment replicas to be 0")
108 108
 	}
109 109
 
110
+	if l, e, a := deployapi.DeploymentConfigAnnotation, config.Name, deployment.Labels[deployapi.DeploymentConfigAnnotation]; e != a {
111
+		t.Fatalf("expected label %s=%s, got %s", l, e, a)
112
+	}
113
+
110 114
 	if e, a := config.Name, deployment.Spec.Template.Labels[deployapi.DeploymentConfigLabel]; e != a {
111 115
 		t.Fatalf("expected label DeploymentConfigLabel=%s, got %s", e, a)
112 116
 	}