1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Do not change external service ports (#1204)

This commit is contained in:
Adam Janikowski 2022-12-09 13:04:58 +01:00 committed by GitHub
parent 237af92b60
commit 3fc881c949
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 220 additions and 14 deletions

View file

@ -40,6 +40,7 @@
- (Bugfix) Wait for Pod to be Ready in post-restart actions
- (Bugfix) Prevent Runtime update restarts
- (Bugfix) Change member port discovery
- (Feature) Do not change external service ports
## [1.2.20](https://github.com/arangodb/kube-arangodb/tree/1.2.20) (2022-10-25)
- (Feature) Add action progress

View file

@ -499,8 +499,6 @@ tools: update-vendor
@GOBIN=$(GOPATH)/bin go install golang.org/x/tools/cmd/goimports@0bb7e5c47b1a31f85d4f173edc878a8e049764a5
@echo ">> Fetching license check"
@GOBIN=$(GOPATH)/bin go install github.com/google/addlicense@6d92264d717064f28b32464f0f9693a5b4ef0239
@echo ">> Fetching GO Assets Builder"
@GOBIN=$(GOPATH)/bin go install github.com/jessevdk/go-assets-builder@b8483521738fd2198ecfc378067a4e8a6079f8e5
@echo ">> Fetching gci"
@GOBIN=$(GOPATH)/bin go install github.com/daixiang0/gci@v0.3.0
@echo ">> Downloading protobuf compiler..."

View file

@ -184,7 +184,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
}
}
} else {
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServicePorts(clientServicePorts), patcher.PatchServiceSelector(clientServiceSelectors)); err != nil {
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServiceOnlyPorts(clientServicePorts...), patcher.PatchServiceSelector(clientServiceSelectors)); err != nil {
log.Err(err).Debug("Failed to patch database client service")
return errors.WithStack(err)
} else if changed {
@ -254,9 +254,12 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
log := r.log.Str("section", "service-ea").Str("role", role).Str("service", eaServiceName)
createExternalAccessService := false
deleteExternalAccessService := false
owned := false
eaServiceType := spec.GetType().AsServiceType() // Note: Type auto defaults to ServiceTypeLoadBalancer
if existing, exists := cachedStatus.Service().V1().GetSimple(eaServiceName); exists {
// External access service exists
owned = apiObject.OwnerOf(existing)
updateExternalAccessService := false
loadBalancerIP := spec.GetLoadBalancerIP()
loadBalancerSourceRanges := spec.LoadBalancerSourceRanges
@ -285,7 +288,7 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
} else if existing.Spec.Type == core.ServiceTypeLoadBalancer && (loadBalancerIP != "" && existing.Spec.LoadBalancerIP != loadBalancerIP) {
deleteExternalAccessService = true // LoadBalancerIP is wrong, remove the current and replace with proper one
createExternalAccessService = true
} else if existing.Spec.Type == core.ServiceTypeNodePort && len(existing.Spec.Ports) == 1 && (nodePort != 0 && existing.Spec.Ports[0].NodePort != int32(nodePort)) {
} else if existing.Spec.Type == core.ServiceTypeNodePort && len(existing.Spec.Ports) < 1 || existing.Spec.Ports[0].Name != shared.ServerPortName && (nodePort != 0 && existing.Spec.Ports[0].NodePort != int32(nodePort)) {
deleteExternalAccessService = true // NodePort is wrong, remove the current and replace with proper one
createExternalAccessService = true
}
@ -300,7 +303,7 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
existing.Spec.LoadBalancerSourceRanges = loadBalancerSourceRanges
}
} else if spec.GetType().IsNodePort() {
if existing.Spec.Type != core.ServiceTypeNodePort || len(existing.Spec.Ports) != 1 || (nodePort != 0 && existing.Spec.Ports[0].NodePort != int32(nodePort)) {
if existing.Spec.Type != core.ServiceTypeNodePort || len(existing.Spec.Ports) < 1 || existing.Spec.Ports[0].Name != shared.ServerPortName || (nodePort != 0 && existing.Spec.Ports[0].NodePort != int32(nodePort)) {
deleteExternalAccessService = true // Remove the current and replace with proper one
createExternalAccessService = true
}
@ -316,7 +319,9 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
}
}
if !createExternalAccessService && !deleteExternalAccessService {
if changed, err := patcher.ServicePatcher(ctx, svcs, existing, meta.PatchOptions{}, patcher.PatchServicePorts(eaPorts), patcher.PatchServiceSelector(eaSelector)); err != nil {
if changed, err := patcher.ServicePatcher(ctx, svcs, existing, meta.PatchOptions{},
patcher.PatchServiceSelector(eaSelector),
patcher.Optional(patcher.PatchServiceOnlyPorts(eaPorts...), owned)); err != nil {
log.Err(err).Debug("Failed to patch database client service")
return errors.WithStack(err)
} else if changed {
@ -331,13 +336,15 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
}
if deleteExternalAccessService {
log.Info("Removing obsolete external access service")
err := globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
return svcs.Delete(ctxChild, eaServiceName, meta.DeleteOptions{})
})
if err != nil {
log.Err(err).Debug("Failed to remove external access service")
return errors.WithStack(err)
if owned {
log.Info("Removing obsolete external access service")
err := globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
return svcs.Delete(ctxChild, eaServiceName, meta.DeleteOptions{})
})
if err != nil {
log.Err(err).Debug("Failed to remove external access service")
return errors.WithStack(err)
}
}
}
if createExternalAccessService {
@ -368,7 +375,7 @@ func (r *Resources) ensureExternalAccessManagedServices(ctx context.Context, cac
apply := func(svc *core.Service) (bool, error) {
return patcher.ServicePatcher(ctx, cachedStatus.ServicesModInterface().V1(), svc, meta.PatchOptions{},
patcher.PatchServicePorts(ports),
patcher.PatchServiceOnlyPorts(ports...),
patcher.PatchServiceSelector(selectors))
}

View file

@ -83,6 +83,72 @@ func PatchServicePorts(ports []core.ServicePort) ServicePatch {
}
}
func Optional(p ServicePatch, enabled bool) ServicePatch {
return func(in *core.Service) []patch.Item {
if !enabled {
return nil
}
if p != nil {
return p(in)
}
return nil
}
}
func PatchServiceOnlyPorts(ports ...core.ServicePort) ServicePatch {
return func(in *core.Service) []patch.Item {
psvc := in.Spec.DeepCopy()
cp := psvc.Ports
changed := false
for pid := range ports {
got := false
for id := range cp {
if ports[pid].Name == cp[id].Name {
got = true
// Set ignored fields
if ports[pid].NodePort == 0 {
ports[pid].NodePort = cp[id].NodePort
}
if ports[pid].AppProtocol == nil {
ports[pid].AppProtocol = cp[id].AppProtocol
}
if ports[pid].Protocol == "" {
ports[pid].Protocol = cp[id].Protocol
}
if ports[pid].TargetPort.StrVal == "" && ports[pid].TargetPort.IntVal == 0 {
ports[pid].TargetPort = cp[id].TargetPort
}
if !equality.Semantic.DeepEqual(ports[pid], cp[id]) {
q := ports[pid].DeepCopy()
cp[id] = *q
changed = true
break
}
}
}
if !got {
q := ports[pid].DeepCopy()
cp = append(cp, *q)
changed = true
}
}
if !changed {
return nil
}
return []patch.Item{
patch.ItemReplace(patch.NewPath("spec", "ports"), cp),
}
}
}
func PatchServiceSelector(selector map[string]string) ServicePatch {
return func(in *core.Service) []patch.Item {
if equality.Semantic.DeepEqual(in.Spec.Selector, selector) {

View file

@ -25,6 +25,9 @@ import (
"github.com/stretchr/testify/require"
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/arangodb/kube-arangodb/pkg/util"
)
func Test_Service_Ports(t *testing.T) {
@ -67,3 +70,134 @@ func Test_Service_Ports(t *testing.T) {
require.Len(t, q, 1)
})
}
func Test_Service_OnlyPorts(t *testing.T) {
t.Run("Equal", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test",
},
},
},
})
require.Len(t, q, 0)
})
t.Run("Missing", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test2",
},
},
},
})
require.Len(t, q, 1)
})
t.Run("Different", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
Port: 8529,
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test1",
},
},
},
})
require.Len(t, q, 1)
})
t.Run("Different Port", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
Port: 8529,
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test",
},
},
},
})
require.Len(t, q, 1)
})
t.Run("Changed NodePort", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
Port: 8529,
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test",
Port: 8529,
NodePort: 12345,
},
},
},
})
require.Len(t, q, 0)
})
t.Run("Changed Port", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
Port: 8528,
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test",
Port: 8529,
NodePort: 12345,
},
},
},
})
require.Len(t, q, 1)
})
t.Run("Ignore fields", func(t *testing.T) {
q := PatchServiceOnlyPorts(core.ServicePort{
Name: "test",
Port: 8528,
})(&core.Service{
Spec: core.ServiceSpec{
Ports: []core.ServicePort{
{
Name: "test",
Protocol: core.ProtocolTCP,
AppProtocol: util.NewString("test"),
Port: 8528,
TargetPort: intstr.IntOrString{
StrVal: "TEST",
IntVal: 0,
},
NodePort: 6543,
},
},
},
})
require.Len(t, q, 0)
})
}