Skip to content
Open
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
16 changes: 16 additions & 0 deletions docs/stack_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,22 @@ spec:
- type: integer
- type: string
x-kubernetes-int-or-string: true
metadata:
description: |-
EmbeddedObjectMetaWithAnnotations defines the metadata which can be attached
to a resource. It's a slimmed down version of metav1.ObjectMeta only
containing annotations.
properties:
annotations:
additionalProperties:
type: string
description: |-
Annotations is an unstructured key value map stored with a resource that may be
set by external tools to store and retrieve arbitrary metadata. They are not
queryable and should be preserved when modifying objects.
More info: http://kubernetes.io/docs/user-guide/annotations
type: object
type: object
required:
- backendPort
type: object
Expand Down
22 changes: 16 additions & 6 deletions docs/stackset_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ spec:
- type: integer
- type: string
x-kubernetes-int-or-string: true
metadata:
description: |-
EmbeddedObjectMetaWithAnnotations defines the metadata which can be attached
to a resource. It's a slimmed down version of metav1.ObjectMeta only
containing annotations.
properties:
annotations:
additionalProperties:
type: string
description: |-
Annotations is an unstructured key value map stored with a resource that may be
set by external tools to store and retrieve arbitrary metadata. They are not
queryable and should be preserved when modifying objects.
More info: http://kubernetes.io/docs/user-guide/annotations
type: object
type: object
required:
- backendPort
type: object
Expand Down Expand Up @@ -3589,12 +3605,6 @@ spec:
Must be set if and only if type is "Localhost".
type: string
type:
description: |-
type indicates which kind of AppArmor profile will be applied.
Valid options are:
Localhost - a profile pre-loaded on the node.
RuntimeDefault - the container runtime's default profile.
Unconfined - no AppArmor enforcement.
type: string
required:
- type
Expand Down
3 changes: 2 additions & 1 deletion pkg/apis/zalando.org/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ func (s *StackSetIngressSpec) GetAnnotations() map[string]string {
// backendport for ingress managed outside of stackset.
// +k8s:deepcopy-gen=true
type StackSetExternalIngressSpec struct {
BackendPort intstr.IntOrString `json:"backendPort"`
EmbeddedObjectMetaWithAnnotations `json:"metadata,omitempty"`
BackendPort intstr.IntOrString `json:"backendPort"`
}

// RouteGroupSpec defines the specification for defining a RouteGroup attached
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/zalando.org/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions pkg/core/stack_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ func objectMetaInjectLabels(objectMeta metav1.ObjectMeta, labels map[string]stri
return objectMeta
}

// patchForwardBackend rewrites a RouteGroupSpec to send all traffic to another cluster chosen by the operator of skipper
func patchForwardBackend(rg *rgv1.RouteGroupSpec) {
rg.Backends = []rgv1.RouteGroupBackend{
{
Name: forwardBackendName,
Type: rgv1.ForwardRouteGroupBackend,
},
}
rg.DefaultBackends = []rgv1.RouteGroupBackendReference{
{
BackendName: forwardBackendName,
},
}
for i := range rg.Routes {
rg.Routes[i].Backends = []rgv1.RouteGroupBackendReference{
{
BackendName: forwardBackendName,
},
}
}
}

func (sc *StackContainer) objectMeta(segment bool) metav1.ObjectMeta {
resourceLabels := mapCopy(sc.Stack.Labels)

Expand Down Expand Up @@ -169,7 +191,15 @@ func (sc *StackContainer) selector() map[string]string {
return limitLabels(sc.Stack.Labels, selectorLabels)
}

// GenerateDeployment generates a deployment as configured in the
// stack. On cluster migrations set by stackset annotation
// "zalando.org/forward-backend", the deployment will be set to
// replicas 1.
func (sc *StackContainer) GenerateDeployment() *appsv1.Deployment {
if _, clusterMigration := sc.Stack.Annotations[forwardBackendAnnotation]; clusterMigration {
return nil
}

stack := sc.Stack

desiredReplicas := sc.stackReplicas
Expand Down Expand Up @@ -224,13 +254,22 @@ func (sc *StackContainer) GenerateDeployment() *appsv1.Deployment {
if strategy != nil {
deployment.Spec.Strategy = *strategy
}

return deployment
}

// GenerateHPA generates a hpa as configured in the
// stack. On cluster migrations set by stackset annotation
// "zalando.org/forward-backend", the hpa will be set to
// minReplicas = maxReplicass = 1.
Copy link
Contributor

Choose a reason for hiding this comment

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

We can just skip generating an HPA in this case, this is similar to if sc.ScaleDown() case below.

Copy link
Member Author

Choose a reason for hiding this comment

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

My fear about removing objects we don't need is that we create alerts for the users and they overreact.
It's also not checking for traffic as we do in ScaleDown()

Copy link
Contributor

Choose a reason for hiding this comment

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

We anyway have to document how this feature works and what to expect.

They could also "overreact" because of minReplicas/maxReplicas = 1 :)

func (sc *StackContainer) GenerateHPA() (
*autoscaling.HorizontalPodAutoscaler,
error,
) {
if _, clusterMigration := sc.Stack.Annotations[forwardBackendAnnotation]; clusterMigration {
return nil, nil
}

autoscalerSpec := sc.Stack.Spec.StackSpec.Autoscaler
trafficWeight := sc.actualTrafficWeight

Expand Down Expand Up @@ -379,6 +418,10 @@ func (sc *StackContainer) GenerateIngressSegment() (
return res, nil
}

// generateIngress generates an ingress as configured in the stack.
// On cluster migrations set by stackset annotation
// "zalando.org/forward-backend", the annotation will be copied to the
// ingress.
func (sc *StackContainer) generateIngress(segment bool) (
*networking.Ingress,
error,
Expand Down Expand Up @@ -430,6 +473,10 @@ func (sc *StackContainer) generateIngress(segment bool) (
Rules: rules,
},
}
if _, clusterMigration := sc.Stack.Annotations[forwardBackendAnnotation]; clusterMigration {
// see https://opensource.zalando.com/skipper/kubernetes/ingress-usage/#skipper-ingress-annotations
result.Annotations["zalando.org/skipper-backend"] = "forward"
}

// insert annotations
result.Annotations = mergeLabels(
Expand Down Expand Up @@ -470,6 +517,10 @@ func (sc *StackContainer) GenerateRouteGroupSegment() (
return res, nil
}

// generateRouteGroup generates an RouteGroup as configured in the
// stack. On cluster migrations set by stackset annotation
// "zalando.org/forward-backend", the RouteGroup will be patched by
// patchForwardBackend() to execute the migration.
func (sc *StackContainer) generateRouteGroup(segment bool) (
*rgv1.RouteGroup,
error,
Expand Down Expand Up @@ -529,6 +580,10 @@ func (sc *StackContainer) generateRouteGroup(segment bool) (
sc.routeGroupSpec.GetAnnotations(),
)

if _, clusterMigration := sc.Stack.Annotations[forwardBackendAnnotation]; clusterMigration {
patchForwardBackend(&result.Spec)
}

return result, nil
}

Expand Down
Loading