Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(controller): don't hardcode experiment ports; always create service #2397

Merged
merged 37 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
952d043
Copy ports from replicaset rather than hardcoding them
alexef Oct 21, 2022
69e9061
Allow user to set the experiment service name.
alexef Oct 21, 2022
d6ae5ad
Run codegen
alexef Oct 21, 2022
666d888
Run codegen again, service name is optional
alexef Oct 21, 2022
431f894
Fix conversion and tests
alexef Oct 21, 2022
49b01f1
Fix codegen
alexef Oct 21, 2022
21adb4a
Fix codegen again
alexef Oct 21, 2022
f534f9c
white space
alexef Oct 21, 2022
2b22947
Extend from corev1.ServiceSpec
alexef Oct 22, 2022
0f28e2b
make lint
alexef Oct 22, 2022
6525b3f
Merge branch 'master' into experiment-service-name
alexef Oct 23, 2022
d805441
infer ports from ReplicaSet. this should fix e2e tests
alexef Oct 23, 2022
5b66607
Merge remote-tracking branch 'origin/experiment-service-name' into ex…
alexef Oct 23, 2022
303b29e
fix e2e
alexef Oct 23, 2022
d1371cd
use json inline so that crd reflects ports
alexef Oct 23, 2022
e1247e1
Add codegen openapi_generated.go
alexef Oct 23, 2022
b8c4157
fix codegen, protobuf is needed
alexef Oct 23, 2022
8c671cc
Merge branch 'master' into experiment-service-name
alexef Oct 24, 2022
de6b858
Add unit test
alexef Oct 24, 2022
bc379f7
Add unit test to new not tested code - copy pods from RS
alexef Oct 24, 2022
9437f6d
Merge branch 'master' into experiment-service-name
alexef Oct 31, 2022
9a179d4
Merge branch 'master' into experiment-service-name
alexef Nov 1, 2022
c32cbe7
Address PR comment: set entire spec
alexef Nov 1, 2022
0f5ed7a
docs: update experiment and rollout spec
alexef Nov 1, 2022
8f46d8c
Merge branch 'master' into experiment-service-name
alexef Nov 1, 2022
95b48ca
Merge branch 'master' into experiment-service-name
alexef Nov 3, 2022
09ac6e1
redo pr. drop spec changes. only inherit ports and always create the …
alexef Nov 4, 2022
9e77224
Merge branch 'master' into experiment-ports
alexef Nov 4, 2022
b262cd1
Ran go fmt
alexef Nov 4, 2022
fcbb31a
revert changes. only enable service if weighted or template service n…
alexef Nov 4, 2022
0c1984d
typo in types
alexef Nov 4, 2022
2a073c5
go fmt. need pre-commit hook
alexef Nov 4, 2022
9d2ce03
no more spec changes
alexef Nov 4, 2022
80eea63
drop test which is not relevant any more
alexef Nov 4, 2022
4a1fa58
Merge branch 'master' into experiment-ports
alexef Nov 4, 2022
8771a96
fix e2e - only generate service if RS has ports
alexef Nov 5, 2022
31cf3d5
fix e2e
alexef Nov 5, 2022
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
10 changes: 8 additions & 2 deletions docs/features/experiment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The Experiment CRD allows users to have ephemeral runs of one or more ReplicaSet
running ephemeral ReplicaSets, the Experiment CRD can launch AnalysisRuns alongside the ReplicaSets.
Generally, those AnalysisRun is used to confirm that new ReplicaSets are running as expected.

A Service routing traffic to the Experiment ReplicaSet is also generated.
alexef marked this conversation as resolved.
Show resolved Hide resolved

## Use cases of Experiments

- A user wants to run two versions of an application for a specific duration to enable Kayenta-style
Expand Down Expand Up @@ -243,5 +245,9 @@ to `experiment-baseline`, leaving the remaining 90% of traffic to the old stack.

!!! note
When a weighted experiment step with traffic routing is used, a
service is auto-created for each experiment template. The traffic routers use
this service to send traffic to the experiment pods.
Service is auto-created for each experiment template. The traffic routers use
this service to send traffic to the experiment pods.

By default, the generated Service has the name of the ReplicaSet and inherits
alexef marked this conversation as resolved.
Show resolved Hide resolved
ports and selector from the specRef definition. It can be accessed in using the `{{templates.baseline.replicaset.name}}`
or `{{templates.canary.replicaset.name}}` variables respectively.
2 changes: 2 additions & 0 deletions docs/features/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ spec:
specRef: stable
- name: canary
specRef: canary
# optional, set the weight of traffic routed to this version
weight: 10
analyses:
- name : mann-whitney
templateName: mann-whitney
Expand Down
14 changes: 13 additions & 1 deletion experiments/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
appslisters "k8s.io/client-go/listers/apps/v1"
v1 "k8s.io/client-go/listers/core/v1"
Expand Down Expand Up @@ -285,8 +286,19 @@ func (ec *experimentContext) createTemplateService(template *v1alpha1.TemplateSp
// Create service with has same name, podTemplateHash, and labels as RS
podTemplateHash := rs.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
svc := ec.templateServices[template.Name]
var ports []corev1.ServicePort
for _, ctr := range rs.Spec.Template.Spec.Containers {
for _, port := range ctr.Ports {
servicePort := corev1.ServicePort{
Protocol: port.Protocol,
Port: port.ContainerPort,
TargetPort: intstr.FromInt(int(port.ContainerPort)),
}
ports = append(ports, servicePort)
}
}
if svc == nil || svc.Name != rs.Name {
newService, err := ec.CreateService(rs.Name, *template, rs.Labels)
newService, err := ec.CreateService(rs.Name, *template, rs.Labels, ports)
if err != nil {
templateStatus.Status = v1alpha1.TemplateStatusError
templateStatus.Message = fmt.Sprintf("Failed to create Service for template '%s': %v", template.Name, err)
Expand Down
23 changes: 23 additions & 0 deletions experiments/experiment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,26 @@ func TestDeleteServiceIfServiceFieldNil(t *testing.T) {
assert.Equal(t, "", exStatus.TemplateStatuses[0].ServiceName)
assert.Nil(t, exCtx.templateServices["bar"])
}

func TestServiceInheritPortsFromRS(t *testing.T) {
templates := generateTemplates("bar")
templates[0].Service = &v1alpha1.TemplateService{}
templates[0].Template.Spec.Containers[0].Ports = []corev1.ContainerPort{
{
Name: "testport",
ContainerPort: 80,
Protocol: "TCP",
},
}
ex := newExperiment("foo", templates, "")

exCtx := newTestContext(ex)
rs := templateToRS(ex, templates[0], 0)
exCtx.templateRSs["bar"] = rs

exCtx.reconcile()

assert.NotNil(t, exCtx.templateServices["bar"])
assert.Equal(t, exCtx.templateServices["bar"].Name, "foo-bar")
assert.Equal(t, exCtx.templateServices["bar"].Spec.Ports[0].Port, int32(80))
}
15 changes: 5 additions & 10 deletions experiments/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
)

var experimentKind = v1alpha1.SchemeGroupVersion.WithKind("Experiment")
Expand Down Expand Up @@ -59,9 +58,9 @@ func GetServiceForExperiment(experiment *v1alpha1.Experiment, svc *corev1.Servic
return nil
}

func (ec *experimentContext) CreateService(serviceName string, template v1alpha1.TemplateSpec, selector map[string]string) (*corev1.Service, error) {
func (ec *experimentContext) CreateService(serviceName string, template v1alpha1.TemplateSpec, selector map[string]string, ports []corev1.ServicePort) (*corev1.Service, error) {
ctx := context.TODO()
serviceAnnotations := newServiceSetAnnotations(ec.ex.Name, template.Name)
serviceAnnotations := newServiceAnnotations(ec.ex.Name, template.Name)
newService := &corev1.Service{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -73,12 +72,8 @@ func (ec *experimentContext) CreateService(serviceName string, template v1alpha1
Annotations: serviceAnnotations,
},
Spec: corev1.ServiceSpec{
Selector: selector,
Ports: []corev1.ServicePort{{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was #2233

Protocol: "TCP",
Port: int32(80),
TargetPort: intstr.FromInt(8080),
}},
Selector: selector,
Ports: ports,
},
}

Expand Down Expand Up @@ -112,7 +107,7 @@ func (ec *experimentContext) deleteService(service corev1.Service) error {
return nil
}

func newServiceSetAnnotations(experimentName, templateName string) map[string]string {
func newServiceAnnotations(experimentName, templateName string) map[string]string {
return map[string]string{
v1alpha1.ExperimentNameAnnotationKey: experimentName,
v1alpha1.ExperimentTemplateNameAnnotationKey: templateName,
Expand Down
4 changes: 1 addition & 3 deletions rollout/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ func GetExperimentFromTemplate(r *v1alpha1.Rollout, stableRS, newRS *appsv1.Repl
Name: templateStep.Name,
Replicas: templateStep.Replicas,
}
if templateStep.Weight != nil {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like we can use weight: 0 to keep creating the service, if automated creation is rolled back

template.Service = &v1alpha1.TemplateService{}
}
template.Service = &v1alpha1.TemplateService{}
templateRS := &appsv1.ReplicaSet{}
switch templateStep.SpecRef {
case v1alpha1.CanarySpecRef:
Expand Down
45 changes: 43 additions & 2 deletions rollout/experiment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ func TestRolloutCreateExperimentWithService(t *testing.T) {
Replicas: pointer.Int32Ptr(1),
Weight: pointer.Int32Ptr(5),
},
// Service should NOT be created for "canary-template"
// Service should also be created for "canary-template"
{
Name: "canary-template",
SpecRef: v1alpha1.CanarySpecRef,
Expand All @@ -818,5 +818,46 @@ func TestRolloutCreateExperimentWithService(t *testing.T) {
assert.NotNil(t, ex.Spec.Templates[0].Service)

assert.Equal(t, "canary-template", ex.Spec.Templates[1].Name)
assert.Nil(t, ex.Spec.Templates[1].Service)
assert.NotNil(t, ex.Spec.Templates[1].Service)
}

func TestRolloutCreateExperimentWithServicePorts(t *testing.T) {
steps := []v1alpha1.CanaryStep{{
Experiment: &v1alpha1.RolloutExperimentStep{
Templates: []v1alpha1.RolloutExperimentTemplate{
// Service should be created for "stable-template"
{
Name: "stable-template",
SpecRef: v1alpha1.StableSpecRef,
Replicas: pointer.Int32Ptr(1),
Weight: pointer.Int32Ptr(5),
},
// Service should also be created for "canary-template"
{
Name: "canary-template",
SpecRef: v1alpha1.CanarySpecRef,
Replicas: pointer.Int32Ptr(1),
},
},
},
}}

r1 := newCanaryRollout("foo", 1, nil, steps, pointer.Int32Ptr(0), intstr.FromInt(0), intstr.FromInt(1))
r2 := bumpVersion(r1)

rs1 := newReplicaSetWithStatus(r1, 1, 1)
rs2 := newReplicaSetWithStatus(r2, 1, 1)
rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]

r2.Status.CurrentStepIndex = pointer.Int32Ptr(0)
r2.Status.StableRS = rs1PodHash

ex, err := GetExperimentFromTemplate(r2, rs1, rs2)
assert.Nil(t, err)

assert.Equal(t, "stable-template", ex.Spec.Templates[0].Name)
assert.NotNil(t, ex.Spec.Templates[0].Service)

assert.Equal(t, "canary-template", ex.Spec.Templates[1].Name)
assert.NotNil(t, ex.Spec.Templates[1].Service)
}
1 change: 0 additions & 1 deletion test/e2e/functional/experiment-dry-run-analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ spec:
templates:
- name: baseline
replicas: 1
service: {}
selector:
matchLabels:
app: experiment-with-dry-run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ spec:
templates:
- name: baseline
replicas: 1
service: {}
selector:
matchLabels:
app: experiment-with-mr
Expand Down
6 changes: 5 additions & 1 deletion test/e2e/functional/experiment-with-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ spec:
templates:
- name: test
replicas: 1
service: {}
service:
ports:
- protocol: TCP
port: 80
targetPort: 8080
selector:
matchLabels:
app: experiment-with-service
Expand Down