Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Feature] Add Gateway Config condition #1959

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

Merged
ajanikow merged 8 commits into master from feature/add_gateway_config
Aug 18, 2025
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- (Bugfix) Helm Lint Indent fix
- (Feature) Add CA Certificates
- (Feature) Chart By Tag filter
- (Feature) Add Gateway Config condition

## [1.3.0](https://github.com/arangodb/kube-arangodb/tree/1.3.0) (2025年08月01日)
- (Feature) (Platform) Storage Debug
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/deployment/v1/conditions.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ const (
ConditionTypeDBServerWithData ConditionType = "DBServerWithData"
// ConditionTypeSyncEnabled Define if DBServer contains any active data leaders
ConditionTypeDBServerWithDataLeader ConditionType = "DBServerWithDataLeader"

// ConditionTypeGatewayConfig contains current config checksum of the Gateway
ConditionTypeGatewayConfig ConditionType = "GatewayConfig"
)

// Condition represents one current condition of a deployment or deployment member.
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/deployment/v2alpha1/conditions.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ const (
ConditionTypeDBServerWithData ConditionType = "DBServerWithData"
// ConditionTypeSyncEnabled Define if DBServer contains any active data leaders
ConditionTypeDBServerWithDataLeader ConditionType = "DBServerWithDataLeader"

// ConditionTypeGatewayConfig contains current config checksum of the Gateway
ConditionTypeGatewayConfig ConditionType = "GatewayConfig"
)

// Condition represents one current condition of a deployment or deployment member.
Expand Down
2 changes: 2 additions & 0 deletions pkg/deployment/client/client.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ type Client interface {
DeleteExpiredJobs(ctx context.Context, timeout time.Duration) error

Compact(ctx context.Context, request *CompactRequest) error

Inventory(ctx context.Context) (*Inventory, error)
}

type client struct {
Expand Down
60 changes: 60 additions & 0 deletions pkg/deployment/client/inventory.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// DISCLAIMER
//
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//

package client

import (
"context"
goHttp "net/http"

utilConstants "github.com/arangodb/kube-arangodb/pkg/util/constants"
)

type Inventory struct {
Configuration InventoryConfiguration `json:"configuration"`
}

type InventoryConfiguration struct {
Hash string `json:"hash"`
}

func (c *client) Inventory(ctx context.Context) (*Inventory, error) {
req, err := c.c.NewRequest(goHttp.MethodGet, utilConstants.EnvoyInventoryConfigDestination)
if err != nil {
return nil, err
}

resp, err := c.c.Do(ctx, req)
if err != nil {
return nil, err
}

if err := resp.CheckStatus(goHttp.StatusOK); err != nil {
return nil, err
}

var l Inventory

if err := resp.ParseBody("", &l); err != nil {
return nil, err
}

return &l, nil
}
4 changes: 2 additions & 2 deletions pkg/deployment/reconcile/context.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ type Context interface {
reconciler.DeploymentImageManager
reconciler.ArangoAgencyGet
reconciler.ArangoApplier
reconciler.DeploymentInfoGetter
reconciler.DeploymentGetter
reconciler.DeploymentDatabaseClient
reconciler.KubernetesEventGenerator

Expand Down
4 changes: 2 additions & 2 deletions pkg/deployment/reconcile/plan_builder_context.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,7 +35,7 @@ import (
type PlanBuilderContext interface {
reconciler.DeploymentStatusUpdate

reconciler.DeploymentInfoGetter
reconciler.DeploymentGetter
reconciler.DeploymentAgencyMaintenance
reconciler.DeploymentPodRenderer
reconciler.DeploymentImageManager
Expand Down
72 changes: 72 additions & 0 deletions pkg/deployment/reconcile/plan_builder_gateway.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// DISCLAIMER
//
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//

package reconcile

import (
"context"
"time"

core "k8s.io/api/core/v1"

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
client "github.com/arangodb/kube-arangodb/pkg/deployment/client"
sharedReconcile "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)

func (r *Reconciler) createMemberGatewayConfigConditionPlan(ctx context.Context, _ k8sutil.APIObject, _ api.DeploymentSpec,
status api.DeploymentStatus, planCtx PlanBuilderContext) api.Plan {
var plan api.Plan

// Check for members in failed state.
for _, m := range status.Members.AsListInGroup(api.ServerGroupGateways) {
inv, err := r.getGatewayInventoryConfig(ctx, planCtx, m.Group, m.Member)
if err != nil {
if c, ok := m.Member.Conditions.Get(api.ConditionTypeGatewayConfig); !ok || c.Status == core.ConditionTrue {
plan = append(plan, sharedReconcile.UpdateMemberConditionActionV2("Config is not present", api.ConditionTypeGatewayConfig, m.Group, m.Member.ID, false, "Config is not present", "Config is not present", ""))
Copy link
Preview

Copilot AI Aug 15, 2025

Choose a reason for hiding this comment

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

The hardcoded string "Config is not present" is repeated multiple times. Consider defining a constant for this message to improve maintainability.

Suggested change
plan = append(plan, sharedReconcile.UpdateMemberConditionActionV2("Config is not present", api.ConditionTypeGatewayConfig, m.Group, m.Member.ID, false, "Config is not present", "Config is not present", ""))
plan = append(plan, sharedReconcile.UpdateMemberConditionActionV2(configNotPresentMsg, api.ConditionTypeGatewayConfig, m.Group, m.Member.ID, false, configNotPresentMsg, configNotPresentMsg, ""))

Copilot uses AI. Check for mistakes.

}

continue
}

logger.JSON("inv", inv).Info("Inventory Fetched")

if c, ok := m.Member.Conditions.Get(api.ConditionTypeGatewayConfig); !ok || c.Status == core.ConditionFalse || c.Hash != inv.Configuration.Hash {
plan = append(plan, sharedReconcile.UpdateMemberConditionActionV2("Config Present", api.ConditionTypeGatewayConfig, m.Group, m.Member.ID, true, "Config Present", "Config Present", inv.Configuration.Hash))
}
}

return plan
}

func (r *Reconciler) getGatewayInventoryConfig(ctx context.Context, planCtx PlanBuilderContext, group api.ServerGroup, member api.MemberStatus) (*client.Inventory, error) {
serverClient, err := planCtx.GetServerClient(ctx, group, member.ID)
if err != nil {
return nil, err
}

Copy link
Preview

Copilot AI Aug 15, 2025

Choose a reason for hiding this comment

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

The variable logger is undefined in this scope. You need to either pass a logger parameter to this function or use a logger from the reconciler context.

Suggested change
logger := log.FromContext(ctx)

Copilot uses AI. Check for mistakes.

internalClient := client.NewClient(serverClient.Connection(), logger)

lCtx, c := context.WithTimeout(ctx, 500*time.Millisecond)
defer c()

return internalClient.Inventory(lCtx)
}
1 change: 1 addition & 0 deletions pkg/deployment/reconcile/plan_builder_high.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func (r *Reconciler) createHighPlan(ctx context.Context, apiObject k8sutil.APIOb
ApplyIfEmpty(r.updateMemberUpdateConditionsPlan).
ApplyIfEmpty(r.updateMemberRotationConditionsPlan).
ApplyIfEmpty(r.createMemberAllowUpgradeConditionPlan).
ApplyIfEmpty(r.createMemberGatewayConfigConditionPlan).
ApplyIfEmpty(r.createMemberRecreationConditionsPlan).
ApplyIfEmpty(r.createMemberPodSchedulingFailurePlan).
ApplyIfEmpty(r.createRotateServerStoragePVCPendingResizeConditionPlan).
Expand Down
10 changes: 10 additions & 0 deletions pkg/deployment/reconcile/plan_builder_test.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ type testContext struct {
state member.StateInspector
}

func (c *testContext) GetServerClient(ctx context.Context, group api.ServerGroup, id string) (driver.Client, error) {
//TODO implement me
panic("implement me")
}

func (c *testContext) GetSyncServerClient(ctx context.Context, group api.ServerGroup, id string) (client.API, error) {
//TODO implement me
panic("implement me")
}

func (c *testContext) IsSyncEnabled() bool {
return false
}
Expand Down
62 changes: 31 additions & 31 deletions pkg/deployment/resources/config_map_gateway.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -53,37 +53,6 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec
return errors.WithStack(errors.Wrapf(err, "Failed to generate gateway config"))
}

_, baseGatewayCfgYamlChecksum, _, err := cfg.RenderYAML()
if err != nil {
return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config"))
}

cfg.Destinations[utilConstants.EnvoyInventoryConfigDestination] = gateway.ConfigDestination{
Type: util.NewType(gateway.ConfigDestinationTypeStatic),
Match: util.NewType(gateway.ConfigMatchPath),
AuthExtension: &gateway.ConfigAuthZExtension{
AuthZExtension: map[string]string{
pbImplEnvoyAuthV3Shared.AuthConfigAuthRequiredKey: pbImplEnvoyAuthV3Shared.AuthConfigKeywordTrue,
pbImplEnvoyAuthV3Shared.AuthConfigAuthPassModeKey: string(networkingApi.ArangoRouteSpecAuthenticationPassModeRemove),
},
},
Static: &gateway.ConfigDestinationStatic[*pbInventoryV1.Inventory]{
Code: util.NewType[uint32](200),
Response: &pbInventoryV1.Inventory{
Configuration: &pbInventoryV1.InventoryConfiguration{
Hash: baseGatewayCfgYamlChecksum,
},
Arangodb: pbInventoryV1.NewArangoDBConfiguration(r.context.GetSpec(), r.context.GetStatus()),
},
Marshaller: ugrpc.Marshal[*pbInventoryV1.Inventory],
Options: []util.Mod[protojson.MarshalOptions]{
func(in *protojson.MarshalOptions) {
in.EmitDefaultValues = true
},
},
},
}

cfg.Destinations[utilConstants.EnvoyIdentityDestination] = gateway.ConfigDestination{
Type: util.NewType(gateway.ConfigDestinationTypeHTTP),
Match: util.NewType(gateway.ConfigMatchPath),
Expand Down Expand Up @@ -138,6 +107,37 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec
},
}

_, baseGatewayCfgYamlChecksum, _, err := cfg.RenderYAML()
if err != nil {
return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config"))
}

cfg.Destinations[utilConstants.EnvoyInventoryConfigDestination] = gateway.ConfigDestination{
Type: util.NewType(gateway.ConfigDestinationTypeStatic),
Match: util.NewType(gateway.ConfigMatchPath),
AuthExtension: &gateway.ConfigAuthZExtension{
AuthZExtension: map[string]string{
pbImplEnvoyAuthV3Shared.AuthConfigAuthRequiredKey: pbImplEnvoyAuthV3Shared.AuthConfigKeywordTrue,
pbImplEnvoyAuthV3Shared.AuthConfigAuthPassModeKey: string(networkingApi.ArangoRouteSpecAuthenticationPassModeRemove),
},
},
Static: &gateway.ConfigDestinationStatic[*pbInventoryV1.Inventory]{
Code: util.NewType[uint32](200),
Response: &pbInventoryV1.Inventory{
Configuration: &pbInventoryV1.InventoryConfiguration{
Hash: baseGatewayCfgYamlChecksum,
},
Arangodb: pbInventoryV1.NewArangoDBConfiguration(r.context.GetSpec(), r.context.GetStatus()),
},
Marshaller: ugrpc.Marshal[*pbInventoryV1.Inventory],
Options: []util.Mod[protojson.MarshalOptions]{
func(in *protojson.MarshalOptions) {
in.EmitDefaultValues = true
},
},
},
}

gatewayCfgYaml, _, _, err := cfg.RenderYAML()
if err != nil {
return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config"))
Expand Down
3 changes: 2 additions & 1 deletion pkg/deployment/resources/config_maps.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
// Copyright 2024-2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -51,5 +51,6 @@ func (r *Resources) EnsureConfigMaps(ctx context.Context, cachedStatus inspector
return errors.Section(err, "Member ConfigMap")
}
}

return reconcileRequired.Reconcile(ctx)
}
12 changes: 10 additions & 2 deletions pkg/util/grpc/grpc.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ type GRPC[T proto.Message] struct {
Object T
}

func (g *GRPC[T]) UnmarshalJSON(data []byte, opts ...util.Mod[protojson.UnmarshalOptions]) error {
func (g *GRPC[T]) UnmarshalJSON(data []byte) error {
return g.UnmarshalJSONOpts(data)
}

func (g *GRPC[T]) UnmarshalJSONOpts(data []byte, opts ...util.Mod[protojson.UnmarshalOptions]) error {
o, err := Unmarshal[T](data, opts...)
if err != nil {
return err
Expand All @@ -166,6 +170,10 @@ func (g *GRPC[T]) UnmarshalJSON(data []byte, opts ...util.Mod[protojson.Unmarsha
return nil
}

func (g GRPC[T]) MarshalJSON(opts ...util.Mod[protojson.MarshalOptions]) ([]byte, error) {
func (g GRPC[T]) MarshalJSON() ([]byte, error) {
return g.MarshalJSONOpts()
}

func (g GRPC[T]) MarshalJSONOpts(opts ...util.Mod[protojson.MarshalOptions]) ([]byte, error) {
return Marshal[T](g.Object, opts...)
}

AltStyle によって変換されたページ (->オリジナル) /