diff --git a/CHANGELOG.md b/CHANGELOG.md index 5744d81b1..1760f0aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) - (Maintenance) Add govulncheck to pipeline, update golangci-linter +- (Feature) Agency Cache memory usage reduction ## [1.2.28](https://github.com/arangodb/kube-arangodb/tree/1.2.28) (2023-06-05) - (Feature) ArangoBackup create retries and MaxIterations limit diff --git a/cmd/cmd.go b/cmd/cmd.go index 2c5d2a70e..30fe8b34e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -49,6 +49,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/api" deploymentApi "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/crd" + "github.com/arangodb/kube-arangodb/pkg/deployment/agency/cache" "github.com/arangodb/kube-arangodb/pkg/deployment/features" "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" "github.com/arangodb/kube-arangodb/pkg/logging" @@ -228,6 +229,9 @@ func init() { if err := features.Init(&cmdMain); err != nil { panic(err.Error()) } + if err := cache.Init(&cmdMain); err != nil { + panic(err.Error()) + } } func Execute() int { diff --git a/pkg/deployment/agency/cache.go b/pkg/deployment/agency/cache.go index 979405b3d..919f7ba5c 100644 --- a/pkg/deployment/agency/cache.go +++ b/pkg/deployment/agency/cache.go @@ -27,22 +27,21 @@ import ( "github.com/rs/zerolog" - "github.com/arangodb/go-driver" - "github.com/arangodb/go-driver/agency" - api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/generated/metric_descriptions" "github.com/arangodb/kube-arangodb/pkg/logging" + "github.com/arangodb/kube-arangodb/pkg/util/arangod/conn" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" "github.com/arangodb/kube-arangodb/pkg/util/metrics" ) +type Connections map[string]conn.Connection + type health struct { namespace, name string leaderID string - leader driver.Connection agencySize int @@ -52,14 +51,6 @@ type health struct { election map[string]int } -func (h health) Leader() (driver.Connection, bool) { - if l := h.leader; l != nil { - return l, true - } - - return nil, false -} - func (h health) CollectMetrics(m metrics.PushMetric) { if err := h.Serving(); err == nil { m.Push(metric_descriptions.ArangodbOperatorAgencyCacheServingGauge(1, h.namespace, h.name)) @@ -145,14 +136,11 @@ type Health interface { // LeaderID returns a leader ID or empty string if a leader is not known. LeaderID() string - // Leader returns connection to the Agency leader - Leader() (driver.Connection, bool) - CollectMetrics(m metrics.PushMetric) } type Cache interface { - Reload(ctx context.Context, size int, clients map[string]agency.Agency) (uint64, error) + Reload(ctx context.Context, size int, clients Connections) (uint64, error) Data() (State, bool) DataDB() (StateDB, bool) CommitIndex() uint64 @@ -206,7 +194,7 @@ func (c cacheSingle) Health() (Health, bool) { return nil, false } -func (c cacheSingle) Reload(_ context.Context, _ int, _ map[string]agency.Agency) (uint64, error) { +func (c cacheSingle) Reload(_ context.Context, _ int, _ Connections) (uint64, error) { return 0, nil } @@ -278,7 +266,7 @@ func (c *cache) Health() (Health, bool) { return nil, false } -func (c *cache) Reload(ctx context.Context, size int, clients map[string]agency.Agency) (uint64, error) { +func (c *cache) Reload(ctx context.Context, size int, clients Connections) (uint64, error) { c.lock.Lock() defer c.lock.Unlock() @@ -313,7 +301,7 @@ func (c *cache) Reload(ctx context.Context, size int, clients map[string]agency. return index, nil } -func (c *cache) reload(ctx context.Context, size int, clients map[string]agency.Agency) (uint64, error) { +func (c *cache) reload(ctx context.Context, size int, clients Connections) (uint64, error) { leaderCli, leaderConfig, health, err := c.getLeader(ctx, size, clients) if err != nil { // Invalidate a leader ID and agency state. @@ -363,7 +351,7 @@ func (c *cache) ShardsInSyncMap() (ShardsSyncStatus, bool) { // getLeader returns config and client to a leader agency, and health to check if agencies are on the same page. // If there is no quorum for the leader then error is returned. -func (c *cache) getLeader(ctx context.Context, size int, clients map[string]agency.Agency) (agency.Agency, *Config, health, error) { +func (c *cache) getLeader(ctx context.Context, size int, clients Connections) (conn.Connection, *Config, health, error) { configs := make([]*Config, len(clients)) errs := make([]error, len(clients)) names := make([]string, 0, len(clients)) @@ -427,7 +415,6 @@ func (c *cache) getLeader(ctx context.Context, size int, clients map[string]agen for id := range names { if h.leaderID == h.names[id] { - h.leader = clients[names[id]].Connection() if cfg := configs[id]; cfg != nil { return clients[names[id]], cfg, h, nil } diff --git a/pkg/deployment/agency/cache/config.go b/pkg/deployment/agency/cache/config.go new file mode 100644 index 000000000..e9956dea4 --- /dev/null +++ b/pkg/deployment/agency/cache/config.go @@ -0,0 +1,55 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 cache + +import ( + "time" + + "github.com/spf13/cobra" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/version" +) + +func Init(cmd *cobra.Command) error { + f := cmd.PersistentFlags() + + ee := version.GetVersionV1().IsEnterprise() + + f.BoolVar(&global.PollEnabled, "agency.poll-enabled", ee, "The Agency poll functionality enablement (EnterpriseEdition Only)") + + if !ee { + if err := f.MarkHidden("agency.poll-enabled"); err != nil { + return err + } + } + + f.DurationVar(&global.RefreshDelay, "agency.refresh-delay", util.BoolSwitch(ee, 500*time.Millisecond, 0), "The Agency refresh delay (0 = no delay)") + + return nil +} + +var global Config + +type Config struct { + PollEnabled bool + RefreshDelay time.Duration +} diff --git a/pkg/deployment/agency/config.go b/pkg/deployment/agency/config.go index cda4cab73..15f977cc4 100644 --- a/pkg/deployment/agency/config.go +++ b/pkg/deployment/agency/config.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 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. @@ -22,41 +22,23 @@ package agency import ( "context" - "encoding/json" "net/http" - "github.com/arangodb/go-driver" - "github.com/arangodb/go-driver/agency" + "github.com/arangodb/kube-arangodb/pkg/util/arangod/conn" + "github.com/arangodb/kube-arangodb/pkg/util/errors" ) -func GetAgencyConfig(ctx context.Context, client agency.Agency) (*Config, error) { - return GetAgencyConfigC(ctx, client.Connection()) -} - -func GetAgencyConfigC(ctx context.Context, conn driver.Connection) (*Config, error) { - req, err := conn.NewRequest(http.MethodGet, "/_api/agency/config") +func GetAgencyConfig(ctx context.Context, connection conn.Connection) (*Config, error) { + resp, code, err := conn.NewExecutor[any, Config](connection).ExecuteGet(ctx, "/_api/agency/config") if err != nil { return nil, err } - var data []byte - - resp, err := conn.Do(driver.WithRawResponse(ctx, &data), req) - if err != nil { - return nil, err + if code != http.StatusOK { + return nil, errors.Newf("Unknown response code %d", code) } - if err := resp.CheckStatus(http.StatusOK); err != nil { - return nil, err - } - - var c Config - - if err := json.Unmarshal(data, &c); err != nil { - return nil, err - } - - return &c, nil + return resp, nil } type Config struct { diff --git a/pkg/deployment/agency/definitions.go b/pkg/deployment/agency/definitions.go index 094360844..6ab39fbd9 100644 --- a/pkg/deployment/agency/definitions.go +++ b/pkg/deployment/agency/definitions.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 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. @@ -25,6 +25,8 @@ import ( "strings" ) +type ReadRequest [][]string + const ( ArangoKey = "arango" ArangoDBKey = "arangodb" @@ -66,6 +68,24 @@ func GetAgencyReadKey(elements ...string) []string { return elements } -func GetAgencyReadRequest(elements ...[]string) [][]string { +func GetAgencyReadRequest(elements ...[]string) ReadRequest { return elements } + +func GetAgencyReadRequestFields() ReadRequest { + return GetAgencyReadRequest([]string{ + GetAgencyKey(ArangoKey, SupervisionKey, SupervisionMaintenanceKey), + GetAgencyKey(ArangoKey, PlanKey, PlanCollectionsKey), + GetAgencyKey(ArangoKey, PlanKey, PlanDatabasesKey), + GetAgencyKey(ArangoKey, CurrentKey, PlanCollectionsKey), + GetAgencyKey(ArangoKey, CurrentKey, CurrentMaintenanceServers), + GetAgencyKey(ArangoKey, TargetKey, TargetHotBackupKey), + GetAgencyKey(ArangoKey, TargetKey, TargetJobToDoKey), + GetAgencyKey(ArangoKey, TargetKey, TargetJobPendingKey), + GetAgencyKey(ArangoKey, TargetKey, TargetJobFailedKey), + GetAgencyKey(ArangoKey, TargetKey, TargetJobFinishedKey), + GetAgencyKey(ArangoKey, TargetKey, TargetCleanedServersKey), + GetAgencyKey(ArangoDBKey, ArangoSyncKey, ArangoSyncStateKey, ArangoSyncStateIncomingKey, ArangoSyncStateIncomingStateKey), + GetAgencyKey(ArangoDBKey, ArangoSyncKey, ArangoSyncStateKey, ArangoSyncStateOutgoingKey, ArangoSyncStateOutgoingTargetsKey), + }) +} diff --git a/pkg/deployment/agency/state.go b/pkg/deployment/agency/state.go index 7902a3762..22dc897c3 100644 --- a/pkg/deployment/agency/state.go +++ b/pkg/deployment/agency/state.go @@ -22,68 +22,31 @@ package agency import ( "context" - "encoding/json" "net/http" - "github.com/arangodb/go-driver" - "github.com/arangodb/go-driver/agency" - + "github.com/arangodb/kube-arangodb/pkg/util/arangod/conn" "github.com/arangodb/kube-arangodb/pkg/util/errors" ) -func (c *cache) loadState(ctx context.Context, client agency.Agency) (StateRoot, error) { - conn := client.Connection() - - req, err := client.Connection().NewRequest(http.MethodPost, "/_api/agency/read") +func (c *cache) loadState(ctx context.Context, connection conn.Connection) (StateRoot, error) { + resp, code, err := conn.NewExecutor[ReadRequest, StateRoots](connection).Execute(ctx, http.MethodPost, "/_api/agency/config", GetAgencyReadRequestFields()) if err != nil { return StateRoot{}, err } - var data []byte - - readKeys := []string{ - GetAgencyKey(ArangoKey, SupervisionKey, SupervisionMaintenanceKey), - GetAgencyKey(ArangoKey, PlanKey, PlanCollectionsKey), - GetAgencyKey(ArangoKey, PlanKey, PlanDatabasesKey), - GetAgencyKey(ArangoKey, CurrentKey, PlanCollectionsKey), - GetAgencyKey(ArangoKey, CurrentKey, CurrentMaintenanceServers), - GetAgencyKey(ArangoKey, TargetKey, TargetHotBackupKey), - GetAgencyKey(ArangoKey, TargetKey, TargetJobToDoKey), - GetAgencyKey(ArangoKey, TargetKey, TargetJobPendingKey), - GetAgencyKey(ArangoKey, TargetKey, TargetJobFailedKey), - GetAgencyKey(ArangoKey, TargetKey, TargetJobFinishedKey), - GetAgencyKey(ArangoKey, TargetKey, TargetCleanedServersKey), - GetAgencyKey(ArangoDBKey, ArangoSyncKey, ArangoSyncStateKey, ArangoSyncStateIncomingKey, ArangoSyncStateIncomingStateKey), - GetAgencyKey(ArangoDBKey, ArangoSyncKey, ArangoSyncStateKey, ArangoSyncStateOutgoingKey, ArangoSyncStateOutgoingTargetsKey), + if code != http.StatusOK { + return StateRoot{}, errors.Newf("Unknown response code %d", code) } - req, err = req.SetBody(GetAgencyReadRequest(GetAgencyReadKey(readKeys...))) - if err != nil { - return StateRoot{}, err + if resp == nil { + return StateRoot{}, errors.Newf("Missing response body") } - resp, err := conn.Do(driver.WithRawResponse(ctx, &data), req) - if err != nil { - return StateRoot{}, err - } - - if err := resp.CheckStatus(http.StatusOK); err != nil { - return StateRoot{}, err - } - - var r StateRoots - - if err := json.Unmarshal(data, &r); err != nil { - return StateRoot{}, err - } - - if len(r) != 1 { + if len(*resp) != 1 { return StateRoot{}, errors.Newf("Invalid response size") } - state := r[0] - - return state, nil + return (*resp)[0], nil } type StateRoots []StateRoot diff --git a/pkg/deployment/client/client_cache.go b/pkg/deployment/client/client_cache.go index 32b1d1ced..7f44bd1b8 100644 --- a/pkg/deployment/client/client_cache.go +++ b/pkg/deployment/client/client_cache.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 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. @@ -43,6 +43,8 @@ type Cache interface { Connection(ctx context.Context, host string) (driver.Connection, error) + GetRaw(group api.ServerGroup, id string) (conn.Connection, error) + Get(ctx context.Context, group api.ServerGroup, id string) (driver.Client, error) GetDatabase(ctx context.Context) (driver.Client, error) GetAgency(ctx context.Context, agencyIDs ...string) (agency.Agency, error) @@ -67,6 +69,19 @@ type cache struct { factory conn.Factory } +func (cc *cache) GetRaw(group api.ServerGroup, id string) (conn.Connection, error) { + cc.mutex.Lock() + defer cc.mutex.Unlock() + m, _, _ := cc.in.GetStatus().Members.ElementByID(id) + + endpoint, err := cc.in.GenerateMemberEndpoint(group, m) + if err != nil { + return nil, err + } + + return cc.factory.RawConnection(endpoint) +} + func (cc *cache) Connection(ctx context.Context, host string) (driver.Connection, error) { return cc.factory.Connection(host) } diff --git a/pkg/deployment/deployment.go b/pkg/deployment/deployment.go index e00b810cf..f9f04563c 100644 --- a/pkg/deployment/deployment.go +++ b/pkg/deployment/deployment.go @@ -34,7 +34,6 @@ import ( "k8s.io/client-go/tools/record" "github.com/arangodb/arangosync-client/client" - agencydriver "github.com/arangodb/go-driver/agency" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/deployment/acs" @@ -185,9 +184,9 @@ func (d *Deployment) RefreshAgencyCache(ctx context.Context) (uint64, error) { rsize := int(*size) - clients := make(map[string]agencydriver.Agency) + clients := agency.Connections{} for _, m := range d.GetStatus().Members.Agents { - a, err := d.GetAgency(lCtx, m.ID) + a, err := d.clientCache.GetRaw(api.ServerGroupAgents, m.ID) if err != nil { return 0, err } diff --git a/pkg/util/arangod/conn/conn.executor.go b/pkg/util/arangod/conn/conn.executor.go new file mode 100644 index 000000000..c3f0f2409 --- /dev/null +++ b/pkg/util/arangod/conn/conn.executor.go @@ -0,0 +1,79 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 conn + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "reflect" +) + +func NewExecutor[IN, OUT interface{}](conn Connection) Executor[IN, OUT] { + return executor[IN, OUT]{ + conn: conn, + } +} + +type executor[IN, OUT interface{}] struct { + conn Connection +} + +func (e executor[IN, OUT]) ExecuteGet(ctx context.Context, endpoint string) (*OUT, int, error) { + var t IN + return e.Execute(ctx, http.MethodGet, endpoint, t) +} + +func (e executor[IN, OUT]) Execute(ctx context.Context, method string, endpoint string, in IN) (*OUT, int, error) { + var reader io.Reader + if q := reflect.ValueOf(in); q.IsValid() && q.IsZero() && q.IsNil() { + data, err := json.Marshal(in) + if err != nil { + return nil, 0, err + } + + reader = bytes.NewReader(data) + } + + resp, code, err := e.conn.Execute(ctx, method, endpoint, reader) + if err != nil { + return nil, 0, err + } + + if resp == nil { + return nil, code, nil + } + + var out OUT + + if err := json.NewDecoder(resp).Decode(&out); err != nil { + return nil, 0, err + } + + return &out, code, err +} + +type Executor[IN, OUT interface{}] interface { + ExecuteGet(ctx context.Context, endpoint string) (*OUT, int, error) + Execute(ctx context.Context, method string, endpoint string, in IN) (*OUT, int, error) +} diff --git a/pkg/util/arangod/conn/conn.go b/pkg/util/arangod/conn/conn.go new file mode 100644 index 000000000..4ee7f9a7d --- /dev/null +++ b/pkg/util/arangod/conn/conn.go @@ -0,0 +1,64 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 conn + +import ( + "context" + "fmt" + "io" + "net/http" +) + +type Connection interface { + Execute(ctx context.Context, method string, endpoint string, body io.Reader) (io.ReadCloser, int, error) +} + +type connection struct { + client *http.Client + + auth *string + + host string +} + +func (c connection) Execute(ctx context.Context, method string, endpoint string, body io.Reader) (io.ReadCloser, int, error) { + req, err := http.NewRequest(method, fmt.Sprintf("%s%s", c.host, endpoint), body) + if err != nil { + return nil, 0, err + } + + req = req.WithContext(ctx) + + if a := c.auth; a != nil { + req.Header.Add("Authorization", *a) + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, 0, err + } + + if b := resp.Body; b != nil { + return b, resp.StatusCode, nil + } + + return nil, resp.StatusCode, nil +} diff --git a/pkg/util/arangod/conn/factory.go b/pkg/util/arangod/conn/factory.go index a26095735..063ddfd4b 100644 --- a/pkg/util/arangod/conn/factory.go +++ b/pkg/util/arangod/conn/factory.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 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. @@ -22,9 +22,14 @@ package conn import ( + http2 "net/http" + "github.com/arangodb/go-driver" "github.com/arangodb/go-driver/agency" "github.com/arangodb/go-driver/http" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/errors" ) type Auth func() (driver.Authentication, error) @@ -37,6 +42,8 @@ type Factory interface { Client(hosts ...string) (driver.Client, error) Agency(hosts ...string) (agency.Agency, error) + RawConnection(host string) (Connection, error) + GetAuth() Auth } @@ -52,6 +59,39 @@ type factory struct { config Config } +func (f factory) RawConnection(host string) (Connection, error) { + cfg, err := f.config() + if err != nil { + return nil, err + } + + var authString *string + + if f.auth != nil { + auth, err := f.auth() + if err != nil { + return nil, err + } + + if auth.Type() != driver.AuthenticationTypeRaw { + return nil, errors.Newf("Only RAW Authentication is supported") + } + + authString = util.NewType(auth.Get("value")) + } + + return connection{ + auth: authString, + host: host, + client: &http2.Client{ + Transport: cfg.Transport, + CheckRedirect: func(req *http2.Request, via []*http2.Request) error { + return http2.ErrUseLastResponse + }, + }, + }, nil +} + func (f factory) GetAuth() Auth { return f.auth } diff --git a/pkg/util/close.go b/pkg/util/close.go new file mode 100644 index 000000000..8e5b04e9a --- /dev/null +++ b/pkg/util/close.go @@ -0,0 +1,54 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 util + +import "sync" + +type Close interface { + Close() error +} + +type closeOnce struct { + lock sync.Mutex + + close Close + + closed bool +} + +func (c *closeOnce) Close() error { + c.lock.Unlock() + defer c.lock.Unlock() + + if c.closed { + return nil + } + + c.closed = true + + return c.close.Close() +} + +func CloseOnce(c Close) Close { + return &closeOnce{ + close: c, + } +} diff --git a/pkg/util/http/requests.go b/pkg/util/http/requests.go new file mode 100644 index 000000000..0adc3c049 --- /dev/null +++ b/pkg/util/http/requests.go @@ -0,0 +1,61 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 http + +import ( + "encoding/json" + "net/http" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/errors" +) + +type RequestInvoker interface { + Do(req *http.Request) (*http.Response, error) +} + +func RequestInvoke[T interface{}](invoker RequestInvoker, request *http.Request) (*T, int, error) { + resp, err := invoker.Do(request) + if err != nil { + return nil, 0, err + } + + if body := resp.Body; body != nil { + c := util.CloseOnce(body) + defer c.Close() + + var obj T + + decoder := json.NewDecoder(body) + + if err := decoder.Decode(&obj); err != nil { + return nil, 0, errors.Wrapf(err, "Unable to decode object") + } + + if err := c.Close(); err != nil { + return nil, 0, err + } + + return &obj, resp.StatusCode, nil + } + + return nil, resp.StatusCode, nil +} diff --git a/pkg/version/version.go b/pkg/version/version.go index 4015c8ef8..0115a34ea 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 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. @@ -54,6 +54,10 @@ type InfoV1 struct { BuildDate string `json:"build_date,omitempty"` } +func (i InfoV1) IsEnterprise() bool { + return i.Edition == EnterpriseEdition +} + func GetVersionV1() InfoV1 { return InfoV1{ Version: driver.Version(version),