Skip to content

Commit

Permalink
Add HealthCheckNodePort deletion logic.
Browse files Browse the repository at this point in the history
Signed-off-by: Xun Jiang <[email protected]>
  • Loading branch information
Xun Jiang committed Oct 26, 2023
1 parent fd8350f commit af3cb8a
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 1 deletion.
65 changes: 65 additions & 0 deletions pkg/restore/service_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*v
if err := deleteNodePorts(service); err != nil {
return nil, err
}
if err := deleteHealthCheckNodePort(service); err != nil {
return nil, err
}

Check warning on line 71 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L70-L71

Added lines #L70 - L71 were not covered by tests
}

res, err := runtime.DefaultUnstructuredConverter.ToUnstructured(service)
Expand All @@ -76,6 +79,68 @@ func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*v
return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil
}

func deleteHealthCheckNodePort(service *corev1api.Service) error {
// Check service type and external traffic policy setting,
// if the setting is not applicable for HealthCheckNodePort, return early.
if service.Spec.ExternalTrafficPolicy != corev1api.ServiceExternalTrafficPolicyTypeLocal ||
service.Spec.Type != corev1api.ServiceTypeLoadBalancer {
return nil
}

// HealthCheckNodePort is already 0, return.
if service.Spec.HealthCheckNodePort == 0 {
return nil
}

Check warning on line 93 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L92-L93

Added lines #L92 - L93 were not covered by tests

// Search HealthCheckNodePort from annotation.
lastAppliedConfig, ok := service.Annotations[annotationLastAppliedConfig]
if ok {
appliedServiceUnstructured := new(map[string]interface{})
if err := json.Unmarshal([]byte(lastAppliedConfig), appliedServiceUnstructured); err != nil {
return errors.WithStack(err)
}

Check warning on line 101 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L98-L101

Added lines #L98 - L101 were not covered by tests

healthCheckNodePort, exist, err := unstructured.NestedInt64(*appliedServiceUnstructured, "spec", "healthCheckNodePort")
if err != nil {
return errors.WithStack(err)
}

Check warning on line 106 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L103-L106

Added lines #L103 - L106 were not covered by tests

// Found healthCheckNodePort in lastAppliedConfig annotation,
// and the value is not 0. No need to delete, return.
if exist && healthCheckNodePort != 0 {
return nil
}

Check warning on line 112 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L110-L112

Added lines #L110 - L112 were not covered by tests
}

// Search HealthCheckNodePort from ManagedFields.
for _, entry := range service.GetManagedFields() {
if entry.FieldsV1 == nil {
continue

Check warning on line 118 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L117-L118

Added lines #L117 - L118 were not covered by tests
}
fields := new(map[string]interface{})
if err := json.Unmarshal(entry.FieldsV1.Raw, fields); err != nil {
return errors.WithStack(err)
}

Check warning on line 123 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L120-L123

Added lines #L120 - L123 were not covered by tests

_, exist, err := unstructured.NestedInt64(*fields, "f:spec", "f:healthCheckNodePort")
if err != nil {
return errors.WithStack(err)
}
if !exist {
continue

Check warning on line 130 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L125-L130

Added lines #L125 - L130 were not covered by tests
}
// Found healthCheckNodePort in ManagedFields,
// and the value is not 0. No need to delete, return.
return nil

Check warning on line 134 in pkg/restore/service_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/service_action.go#L134

Added line #L134 was not covered by tests
}

// Cannot find HealthCheckNodePort from Annotation and
// ManagedFields, which means it's auto-generated. Delete it.
service.Spec.HealthCheckNodePort = 0

return nil
}

func deleteNodePorts(service *corev1api.Service) error {
if service.Spec.Type == corev1api.ServiceTypeExternalName {
return nil
Expand Down
161 changes: 160 additions & 1 deletion pkg/restore/service_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import (
func svcJSON(ports ...corev1api.ServicePort) string {
svc := corev1api.Service{
Spec: corev1api.ServiceSpec{
Ports: ports,
HealthCheckNodePort: 8080,
Ports: ports,
},
}

Expand Down Expand Up @@ -486,6 +487,164 @@ func TestServiceActionExecute(t *testing.T) {
},
},
},
{
name: "If PreserveNodePorts is True in restore spec then HealthCheckNodePort always preserved.",
obj: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeLocal,
Type: corev1api.ServiceTypeLoadBalancer,
Ports: []corev1api.ServicePort{
{
Name: "http",
Port: 80,
NodePort: 8080,
},
{
Name: "hepsiburada",
NodePort: 9025,
},
},
},
},
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(true).Result(),
expectedRes: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeLocal,
Type: corev1api.ServiceTypeLoadBalancer,
Ports: []corev1api.ServicePort{
{
Name: "http",
Port: 80,
NodePort: 8080,
},
{
Name: "hepsiburada",
NodePort: 9025,
},
},
},
},
},
{
name: "If PreserveNodePorts is False in restore spec then HealthCheckNodePort should be cleaned.",
obj: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeLocal,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(false).Result(),
expectedRes: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 0,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeLocal,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
},
{
name: "If PreserveNodePorts is false in restore spec, but service is not expected, then HealthCheckNodePort should be kept.",
obj: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(false).Result(),
expectedRes: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
},
{
name: "If PreserveNodePorts is false in restore spec, but HealthCheckNodePort can be found in Annotation, then it should be kept.",
obj: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
Annotations: map[string]string{annotationLastAppliedConfig: svcJSON()},
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(false).Result(),
expectedRes: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
Annotations: map[string]string{annotationLastAppliedConfig: svcJSON()},
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
},
{
name: "If PreserveNodePorts is false in restore spec, but HealthCheckNodePort can be found in ManagedFields, then it should be kept.",
obj: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
ManagedFields: []metav1.ManagedFieldsEntry{
{
FieldsV1: &metav1.FieldsV1{
Raw: []byte(`{"f:spec":{"f:healthCheckNodePort":{}}}`),
},
},
},
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(false).Result(),
expectedRes: corev1api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc-1",
ManagedFields: []metav1.ManagedFieldsEntry{
{
FieldsV1: &metav1.FieldsV1{
Raw: []byte(`{"f:spec":{"f:healthCheckNodePort":{}}}`),
},
},
},
},
Spec: corev1api.ServiceSpec{
HealthCheckNodePort: 8080,
ExternalTrafficPolicy: corev1api.ServiceExternalTrafficPolicyTypeCluster,
Type: corev1api.ServiceTypeLoadBalancer,
},
},
},
}

for _, test := range tests {
Expand Down

0 comments on commit af3cb8a

Please sign in to comment.