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

[Feature] Namespaced mode (#615)

This commit is contained in:
Adam Janikowski 2020-08-14 09:35:11 +02:00 committed by GitHub
parent e979b0c2ef
commit 145d71dde6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 176 additions and 91 deletions

View file

@ -1,6 +1,7 @@
# Change Log
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
- Add Operator Namespaced mode (Alpha)
## [1.0.5](https://github.com/arangodb/kube-arangodb/tree/1.0.5) (2020-08-05)
- Add Labels and Annotations to ServiceMonitor

View file

@ -75,6 +75,7 @@ Default: `legacy`
Supported modes:
- `legacy` - mode with limited cluster scope access
- `namespaced` - mode with namespace access only
### `operator.service.type`

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.backup -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -20,6 +21,6 @@ subjects:
name: {{ template "kube-arangodb.operatorName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.backup -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -15,5 +16,7 @@ rules:
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch"]
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.deployment -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -20,6 +21,6 @@ subjects:
name: {{ template "kube-arangodb.operatorName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.deployment -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -18,9 +19,7 @@ rules:
- apiGroups: [""]
resources: ["namespaces", "nodes", "persistentvolumes"]
verbs: ["get", "list"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list"]
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.deploymentReplications -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -20,6 +21,6 @@ subjects:
name: {{ template "kube-arangodb.operatorName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,5 @@
{{ if .Values.rbac.enabled -}}
{{ if not (eq .Values.operator.scope "namespaced") -}}
{{ if .Values.operator.features.deploymentReplications -}}
apiVersion: rbac.authorization.k8s.io/v1
@ -14,10 +15,11 @@ metadata:
rules:
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["namespaces", "nodes"]
verbs: ["get", "list"]
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,8 +1,13 @@
{{- if eq .Values.operator.scope "legacy" -}}
{{ if eq .Values.operator.scope "legacy" -}}
# Scope "legacy" selected
{{ else if eq .Values.operator.scope "namespaced" -}}
# Scope "namespaced" selected
{{ if .Values.operator.features.storage -}}
{{ fail (printf "Storage Operator not supported in %s scope!" .Values.operator.scope) -}}
{{ end -}}
{{ else -}}
{{ fail (printf "Operator Scope %s is not supported!" .Values.operator.scope) }}
{{- end -}}
{{ fail (printf "Operator Scope %s is not supported!" .Values.operator.scope) -}}
{{ end -}}
apiVersion: apps/v1
kind: Deployment
metadata:

View file

@ -20,6 +20,5 @@ subjects:
name: {{ template "kube-arangodb.operatorName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}

View file

@ -17,7 +17,7 @@ rules:
verbs: ["*"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["namespaces", "nodes"]
verbs: ["get", "list"]
@ -27,5 +27,6 @@ rules:
- apiGroups: ["storage.arangodb.com"]
resources: ["arangolocalstorages"]
verbs: ["*"]
{{- end }}
{{- end }}

View file

@ -31,6 +31,8 @@ import (
"strings"
"time"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/rs/zerolog/log"
@ -139,7 +141,7 @@ func init() {
f.StringVar(&operatorOptions.arangoImage, "operator.arango-image", ArangoImageEnv.GetOrDefault(defaultArangoImage), "Docker image used for arango by default")
f.BoolVar(&chaosOptions.allowed, "chaos.allowed", false, "Set to allow chaos in deployments. Only activated when allowed and enabled in deployment")
f.BoolVar(&operatorOptions.singleMode, "mode.single", false, "Enable single mode in Operator. WARNING: There should be only one replica of Operator, otherwise Operator can take unexpected actions")
f.StringVar(&operatorOptions.scope, "scope", operator.DefaultScope.String(), "Define scope on which Operator works. Legacy - pre 1.1.0 scope with limited cluster access")
f.StringVar(&operatorOptions.scope, "scope", scope.DefaultScope.String(), "Define scope on which Operator works. Legacy - pre 1.1.0 scope with limited cluster access")
features.Init(&cmdMain)
}
@ -299,7 +301,7 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper
}
eventRecorder := createRecorder(cliLog, kubecli, name, namespace)
scope, ok := operator.AsScope(operatorOptions.scope)
scope, ok := scope.AsScope(operatorOptions.scope)
if !ok {
return operator.Config{}, operator.Dependencies{}, maskAny(fmt.Errorf("Scope %s is not known by Operator", operatorOptions.scope))
}

View file

@ -31,6 +31,8 @@ import (
"strconv"
"time"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
monitoringClient "github.com/coreos/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
@ -84,6 +86,10 @@ func (d *Deployment) GetMonitoringV1Cli() monitoringClient.MonitoringV1Interface
return d.deps.KubeMonitoringCli
}
func (d *Deployment) GetScope() scope.Scope {
return d.config.Scope
}
// GetLifecycleImage returns the image name containing the lifecycle helper (== name of operator image)
func (d *Deployment) GetLifecycleImage() string {
return d.config.LifecycleImage
@ -425,16 +431,6 @@ func (d *Deployment) UpdatePvc(pvc *v1.PersistentVolumeClaim) error {
return maskAny(err)
}
// GetPv returns PV info about PV with given name.
func (d *Deployment) GetPv(pvName string) (*v1.PersistentVolume, error) {
pv, err := d.GetKubeCli().CoreV1().PersistentVolumes().Get(pvName, meta.GetOptions{})
if err == nil {
return pv, nil
}
return nil, maskAny(err)
}
// GetOwnedPVCs returns a list of all PVCs owned by the deployment.
func (d *Deployment) GetOwnedPVCs() ([]v1.PersistentVolumeClaim, error) {
// Get all current PVCs

View file

@ -29,6 +29,8 @@ import (
"sync/atomic"
"time"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
monitoringClient "github.com/coreos/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
"github.com/arangodb/kube-arangodb/pkg/util/arangod/conn"
@ -63,6 +65,7 @@ type Config struct {
OperatorUUIDInitImage string
MetricsExporterImage string
ArangoImage string
Scope scope.Scope
}
// Dependencies holds dependent services for a Deployment
@ -466,7 +469,12 @@ func (d *Deployment) isOwnerOf(obj metav1.Object) bool {
// once at creation time of the deployment and then always if the CRD
// informer is triggered.
func (d *Deployment) lookForServiceMonitorCRD() {
_, err := d.deps.KubeExtCli.ApiextensionsV1beta1().CustomResourceDefinitions().Get("servicemonitors.monitoring.coreos.com", metav1.GetOptions{})
var err error
if d.GetScope().IsNamespaced() {
_, err = d.deps.KubeMonitoringCli.ServiceMonitors(d.GetNamespace()).List(metav1.ListOptions{})
} else {
_, err = d.deps.KubeExtCli.ApiextensionsV1beta1().CustomResourceDefinitions().Get("servicemonitors.monitoring.coreos.com", metav1.GetOptions{})
}
log := d.deps.Log
log.Debug().Msgf("Looking for ServiceMonitor CRD...")
if err == nil {

View file

@ -84,8 +84,6 @@ type ActionContext interface {
// GetPvc returns PVC info about PVC with given name in the namespace
// of the deployment.
GetPvc(pvcName string) (*v1.PersistentVolumeClaim, error)
// GetPv returns PV info about PV with given name.
GetPv(pvName string) (*v1.PersistentVolume, error)
// UpdatePvc update PVC with given name in the namespace
// of the deployment.
UpdatePvc(pvc *v1.PersistentVolumeClaim) error
@ -187,10 +185,6 @@ func (ac *actionContext) UpdateClusterCondition(conditionType api.ConditionType,
})
}
func (ac *actionContext) GetPv(pvName string) (*v1.PersistentVolume, error) {
return ac.context.GetPv(pvName)
}
func (ac *actionContext) GetAPIObject() k8sutil.APIObject {
return ac.context.GetAPIObject()
}

View file

@ -129,17 +129,8 @@ func (a *actionPVCResize) CheckProgress(ctx context.Context) (bool, bool, error)
return false, true, err
}
pv, err := a.actionCtx.GetPv(pvc.Spec.VolumeName)
if err != nil {
if errors.IsNotFound(err) {
return true, false, nil
}
return false, true, err
}
if requestedSize, ok := pvc.Spec.Resources.Requests[core.ResourceStorage]; ok {
if volumeSize, ok := pv.Spec.Capacity[core.ResourceStorage]; ok {
if volumeSize, ok := pvc.Status.Capacity[core.ResourceStorage]; ok {
cmp := volumeSize.Cmp(requestedSize)
if cmp >= 0 {
return true, false, nil

View file

@ -83,8 +83,6 @@ type Context interface {
UpdatePvc(pvc *v1.PersistentVolumeClaim) error
// GetPvc gets a PVC by the given name, in the samespace of the deployment.
GetPvc(pvcName string) (*v1.PersistentVolumeClaim, error)
// GetPv returns PV info about PV with given name.
GetPv(pvName string) (*v1.PersistentVolume, error)
// GetTLSKeyfile returns the keyfile encoded TLS certificate+key for
// the given member.
GetTLSKeyfile(group api.ServerGroup, member api.MemberStatus) (string, error)

View file

@ -25,6 +25,8 @@ package resources
import (
"context"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
monitoringClient "github.com/coreos/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
@ -100,4 +102,5 @@ type Context interface {
WithStatusUpdate(action func(s *api.DeploymentStatus) bool, force ...bool) error
// GetBackup receives information about a backup resource
GetBackup(backup string) (*backupApi.ArangoBackup, error)
GetScope() scope.Scope
}

View file

@ -54,9 +54,9 @@ func (r *Resources) prepareAgencyPodTermination(ctx context.Context, log zerolog
return nil
}
// Check node the pod is scheduled on
// Check node the pod is scheduled on. Only if not in namespaced scope
agentDataWillBeGone := false
if p.Spec.NodeName != "" {
if !r.context.GetScope().IsNamespaced() && p.Spec.NodeName != "" {
node, err := r.context.GetKubeCli().CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
if k8sutil.IsNotFound(err) {
log.Warn().Msg("Node not found")
@ -165,7 +165,7 @@ func (r *Resources) prepareDBServerPodTermination(ctx context.Context, log zerol
// Check node the pod is scheduled on
dbserverDataWillBeGone := false
if p.Spec.NodeName != "" {
if !r.context.GetScope().IsNamespaced() && p.Spec.NodeName != "" {
node, err := r.context.GetKubeCli().CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
if k8sutil.IsNotFound(err) {
log.Warn().Msg("Node not found")

View file

@ -28,6 +28,7 @@ import (
"github.com/arangodb/kube-arangodb/pkg/apis/replication"
lsapi "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha"
"github.com/arangodb/kube-arangodb/pkg/util/crd"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// waitForCRD waits for the CustomResourceDefinition (created externally)
@ -35,31 +36,63 @@ import (
func (o *Operator) waitForCRD(enableDeployment, enableDeploymentReplication, enableStorage, enableBackup bool) error {
log := o.log
if enableDeployment {
log.Debug().Msg("Waiting for ArangoDeployment CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, deployment.ArangoDeploymentCRDName); err != nil {
return maskAny(err)
if o.Scope.IsNamespaced() {
if enableDeployment {
log.Debug().Msg("Waiting for ArangoDeployment CRD to be ready")
if err := crd.WaitReady(func() error {
_, err := o.CRCli.DatabaseV1().ArangoDeployments(o.Namespace).List(meta.ListOptions{})
return err
}); err != nil {
return maskAny(err)
}
}
}
if enableDeploymentReplication {
log.Debug().Msg("Waiting for ArangoDeploymentReplication CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, replication.ArangoDeploymentReplicationCRDName); err != nil {
return maskAny(err)
if enableDeploymentReplication {
log.Debug().Msg("Waiting for ArangoDeploymentReplication CRD to be ready")
if err := crd.WaitReady(func() error {
_, err := o.CRCli.ReplicationV1().ArangoDeploymentReplications(o.Namespace).List(meta.ListOptions{})
return err
}); err != nil {
return maskAny(err)
}
}
}
if enableStorage {
log.Debug().Msg("Waiting for ArangoLocalStorage CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, lsapi.ArangoLocalStorageCRDName); err != nil {
return maskAny(err)
if enableBackup {
log.Debug().Msg("Wait for ArangoBackup CRD to be ready")
if err := crd.WaitReady(func() error {
_, err := o.CRCli.BackupV1().ArangoBackups(o.Namespace).List(meta.ListOptions{})
return err
}); err != nil {
return maskAny(err)
}
}
} else {
if enableDeployment {
log.Debug().Msg("Waiting for ArangoDeployment CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, deployment.ArangoDeploymentCRDName); err != nil {
return maskAny(err)
}
}
}
if enableBackup {
log.Debug().Msg("Wait for ArangoBackup CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, backup.ArangoBackupCRDName); err != nil {
return maskAny(err)
if enableDeploymentReplication {
log.Debug().Msg("Waiting for ArangoDeploymentReplication CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, replication.ArangoDeploymentReplicationCRDName); err != nil {
return maskAny(err)
}
}
if enableStorage {
log.Debug().Msg("Waiting for ArangoLocalStorage CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, lsapi.ArangoLocalStorageCRDName); err != nil {
return maskAny(err)
}
}
if enableBackup {
log.Debug().Msg("Wait for ArangoBackup CRD to be ready")
if err := crd.WaitCRDReady(o.KubeExtCli, backup.ArangoBackupCRDName); err != nil {
return maskAny(err)
}
}
}

View file

@ -27,6 +27,8 @@ import (
"math/rand"
"time"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
monitoringClient "github.com/coreos/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
"github.com/arangodb/kube-arangodb/pkg/backup/operator/event"
@ -63,27 +65,6 @@ const (
initRetryWaitTime = 30 * time.Second
)
func AsScope(s string) (Scope, bool) {
switch s {
case LegacyScope.String():
return LegacyScope, true
}
return "", false
}
type Scope string
func (s Scope) String() string {
return string(s)
}
const (
LegacyScope Scope = "legacy"
DefaultScope = LegacyScope
)
type Event struct {
Type kwatch.EventType
Deployment *deplapi.ArangoDeployment
@ -116,7 +97,7 @@ type Config struct {
EnableBackup bool
AllowChaos bool
SingleMode bool
Scope Scope
Scope scope.Scope
}
type Dependencies struct {

View file

@ -210,6 +210,7 @@ func (o *Operator) makeDeploymentConfigAndDeps(apiObject *api.ArangoDeployment)
MetricsExporterImage: o.MetricsExporterImage,
ArangoImage: o.ArangoImage,
AllowChaos: o.Config.AllowChaos,
Scope: o.Scope,
}
deps := deployment.Dependencies{
Log: o.Dependencies.LogService.MustGetLogger("deployment").With().

View file

@ -0,0 +1,51 @@
//
// DISCLAIMER
//
// Copyright 2020 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
//
// Author Adam Janikowski
//
package scope
func AsScope(s string) (Scope, bool) {
switch s {
case LegacyScope.String():
return LegacyScope, true
case NamespacedScope.String():
return NamespacedScope, true
}
return "", false
}
type Scope string
func (s Scope) String() string {
return string(s)
}
func (s Scope) IsNamespaced() bool {
return s == NamespacedScope
}
const (
LegacyScope Scope = "legacy"
NamespacedScope Scope = "namespaced"
DefaultScope = LegacyScope
)

View file

@ -44,6 +44,11 @@ const (
// FindOtherOperators looks up references to other operators in the same Kubernetes cluster.
func (o *Operator) FindOtherOperators() []server.OperatorReference {
if o.Scope.IsNamespaced() {
// In namespaced scope nothing to do
return []server.OperatorReference{}
}
log := o.log
var result []server.OperatorReference
namespaces, err := o.Dependencies.KubeCli.CoreV1().Namespaces().List(metav1.ListOptions{})
@ -94,6 +99,9 @@ func (o *Operator) findOtherOperatorsInNamespace(log zerolog.Logger, namespace s
return nil
}
nodeFetcher := func() (v1.NodeList, error) {
if o.Scope.IsNamespaced() {
return v1.NodeList{}, nil
}
result, err := o.Dependencies.KubeCli.CoreV1().Nodes().List(metav1.ListOptions{})
if err != nil {
return v1.NodeList{}, maskAny(err)

View file

@ -33,6 +33,14 @@ import (
"github.com/arangodb/kube-arangodb/pkg/util/retry"
)
// WaitReady waits for a check to be ready.
func WaitReady(check func() error) error {
if err := retry.Retry(check, time.Second*30); err != nil {
return maskAny(err)
}
return nil
}
// WaitCRDReady waits for a custom resource definition with given name to be ready.
func WaitCRDReady(clientset apiextensionsclient.Interface, crdName string) error {
op := func() error {
@ -54,8 +62,5 @@ func WaitCRDReady(clientset apiextensionsclient.Interface, crdName string) error
}
return maskAny(fmt.Errorf("Retry needed"))
}
if err := retry.Retry(op, time.Second*30); err != nil {
return maskAny(err)
}
return nil
return WaitReady(op)
}