Skip to content

Commit

Permalink
feat: add options struct and prune client (#20)
Browse files Browse the repository at this point in the history
whg517 authored May 23, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent d18b933 commit 74e4b13
Showing 35 changed files with 1,201 additions and 836 deletions.
55 changes: 51 additions & 4 deletions api/v1alpha1/clusterconfig_types.go
Original file line number Diff line number Diff line change
@@ -7,10 +7,57 @@ type ClusterConfigSpec struct {
// +kubebuilder:validation:Required
Redis *RedisSpec `json:"redis"`

// +kubebuilder:validation:Required
Administrator *AdministratorSpec `json:"administrator"`

// +kubebuilder:validation:Optional
// This is flask app secret key
AppSecretKey *AppSecretKeySpec `json:"appSecretKey,omitempty"`

// +kubebuilder:validation:Optional
ListenerClass string `json:"listenerClass,omitempty"`
}

// AppSecretKeySpec defines the app secret key spec.
type AppSecretKeySpec struct {
// +kubebuilder:validation=Optional
// ExistSecret is the name of the secret that contains the secret key.
// It must contain the key `SUPERSET_SECRET_KEY`.
// Note: To avoid the key name confusions, the key name must be started with `SUPERSET_`.
ExistSecret string `json:"existSecret,omitempty"`
// +kubebuilder:validation=Optional
// If value is not set, the secret will be generated.
// When you migrate the Superset instance, you should keep the same secret key in the new instance.
SecretKey string `json:"secretKey,omitempty"`
}

type AdministratorSpec struct {
// +kubebuilder:validation=Optional
// +kubebuilder:default="admin"
Username string `json:"username,omitempty"`
// +kubebuilder:validation=Optional
// +kubebuilder:default="Superset"
FirstName string `json:"firstName,omitempty"`
// +kubebuilder:validation=Optional
// +kubebuilder:default="Admin"
LastName string `json:"lastName,omitempty"`
// +kubebuilder:validation=Optional
// +kubebuilder:default="admin@superset"
Email string `json:"email,omitempty"`
// +kubebuilder:validation=Optional
// +kubebuilder:default="admin"
Password string `json:"password,omitempty"`
// +kubebuilder:validation=Optional
// ExistSecret is the name of the secret that contains the administrator info.
// It must contain the following keys:
// - `ADMIN_USERNAME`
// - `ADMIN_FIRST_NAME`
// - `ADMIN_LAST_NAME`
// - `ADMIN_EMAIL`
// - `ADMIN_PASSWORD`
ExistSecret string `json:"existSecret,omitempty"`
}

// RedisSpec defines the redis spec.
type RedisSpec struct {
// +kubebuilder:validation=Optional
@@ -37,7 +84,7 @@ type RedisSpec struct {

type DatabaseSpec struct {
// +kubebuilder:validation=Optional
Reference string `json:"reference"`
Reference *string `json:"reference,omitempty"`

// +kubebuilder:validation=Optional
Inline *DatabaseInlineSpec `json:"inline,omitempty"`
@@ -50,15 +97,15 @@ type DatabaseInlineSpec struct {
Driver string `json:"driver,omitempty"`

// +kubebuilder:validation=Optional
// +kubebuilder:default="hive"
// +kubebuilder:default="superset"
DatabaseName string `json:"databaseName,omitempty"`

// +kubebuilder:validation=Optional
// +kubebuilder:default="hive"
// +kubebuilder:default="superset"
Username string `json:"username,omitempty"`

// +kubebuilder:validation=Optional
// +kubebuilder:default="hive"
// +kubebuilder:default="superset"
Password string `json:"password,omitempty"`

// +kubebuilder:validation=Required
10 changes: 7 additions & 3 deletions api/v1alpha1/image_types.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package v1alpha1

type ImageSpec struct {
Custom string `json:"custom,omitempty"`
Repo string `json:"repo,omitempty"`
KDSVersion string `json:"kdsVersion,omitempty"`
// +kubebuilder:validation:Optional
Custom string `json:"custom,omitempty"`
// +kubebuilder:validation:Optional
Repo string `json:"repo,omitempty"`
// +kubebuilder:validation:Optional
KDSVersion string `json:"kdsVersion,omitempty"`
// +kubebuilder:validation:Optional
ProductVersion string `json:"productVersion,omitempty"`
}
2 changes: 1 addition & 1 deletion api/v1alpha1/supersetcluster_types.go
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ import (
type SupersetClusterSpec struct {
Image *ImageSpec `json:"image,omitempty"`
ClusterConfig *ClusterConfigSpec `json:"clusterConfig"`
ClusterOperation *apiv1alpha1.ClusterOperationSpec `json:"clusterOperation"`
ClusterOperation *apiv1alpha1.ClusterOperationSpec `json:"clusterOperation,omitempty"`
Node *NodeSpec `json:"node"`
Worker *WorkerSpec `json:"worker"`
}
45 changes: 45 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 5 additions & 11 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ package main

import (
"flag"
"fmt"
"os"
"strings"

@@ -66,12 +65,6 @@ func main() {

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

watchNamespaces, err := getWatchNamespaces()
if err != nil {
setupLog.Error(err, "unable to get watch namespaces")
os.Exit(1)
}

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: server.Options{BindAddress: metricsAddr},
@@ -89,7 +82,7 @@ func main() {
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
Cache: cache.Options{DefaultNamespaces: watchNamespaces},
Cache: cache.Options{DefaultNamespaces: getWatchNamespaces()},
})
if err != nil {
setupLog.Error(err, "unable to start manager")
@@ -122,15 +115,16 @@ func main() {
}

// getWatchNamespaces returns the Namespaces the operator should be watching for changes
func getWatchNamespaces() (map[string]cache.Config, error) {
func getWatchNamespaces() map[string]cache.Config {
// WatchNamespacesEnvVar is the constant for env variable WATCH_NAMESPACES
// which specifies the Namespaces to watch.
// An empty value means the operator is running with cluster scope.
var watchNamespacesEnvVar = "WATCH_NAMESPACES"

ns, found := os.LookupEnv(watchNamespacesEnvVar)
if !found {
return nil, fmt.Errorf("%s must be set", watchNamespacesEnvVar)
setupLog.Info("watchNamespaces", "namespaces", "all")
return nil
}
nss := cleanNamespaceList(ns)

@@ -146,7 +140,7 @@ func getWatchNamespaces() (map[string]cache.Config, error) {
setupLog.Info("watchNamespaces", "namespaces", "all")
}

return cachedNamespaces, nil
return cachedNamespaces
}

func cleanNamespaceList(namespaces string) (result []string) {
49 changes: 43 additions & 6 deletions config/crd/bases/superset.zncdata.dev_supersetclusters.yaml
Original file line number Diff line number Diff line change
@@ -36,14 +36,53 @@ spec:
properties:
clusterConfig:
properties:
administrator:
properties:
email:
default: admin@superset
type: string
existSecret:
description: 'ExistSecret is the name of the secret that contains
the administrator info. It must contain the following keys:
- `ADMIN_USERNAME` - `ADMIN_FIRST_NAME` - `ADMIN_LAST_NAME`
- `ADMIN_EMAIL` - `ADMIN_PASSWORD`'
type: string
firstName:
default: Superset
type: string
lastName:
default: Admin
type: string
password:
default: admin
type: string
username:
default: admin
type: string
type: object
appSecretKey:
description: This is flask app secret key
properties:
existSecret:
description: 'ExistSecret is the name of the secret that contains
the secret key. It must contain the key `SUPERSET_SECRET_KEY`.
Note: To avoid the key name confusions, the key name must
be started with `SUPERSET_`.'
type: string
secretKey:
description: If value is not set, the secret will be generated.
When you migrate the Superset instance, you should keep
the same secret key in the new instance.
type: string
type: object
database:
properties:
inline:
description: DatabaseInlineSpec defines the inline database
spec.
properties:
databaseName:
default: hive
default: superset
type: string
driver:
default: postgres
@@ -54,20 +93,18 @@ spec:
host:
type: string
password:
default: hive
default: superset
type: string
port:
default: 5432
format: int32
type: integer
username:
default: hive
default: superset
type: string
type: object
reference:
type: string
required:
- reference
type: object
listenerClass:
type: string
@@ -101,6 +138,7 @@ spec:
- host
type: object
required:
- administrator
- database
- redis
type: object
@@ -5403,7 +5441,6 @@ spec:
type: object
required:
- clusterConfig
- clusterOperation
- node
- worker
type: object
24 changes: 23 additions & 1 deletion config/samples/superset_v1alpha1_supersetcluster.yaml
Original file line number Diff line number Diff line change
@@ -9,4 +9,26 @@ metadata:
app.kubernetes.io/created-by: superset-operator
name: supersetcluster-sample
spec:
# TODO(user): Add fields here
image:
custom: apache/superset:4.0.1
clusterConfig:
database:
inline:
driver: postgres
databaseName: superset
username: superset
password: superset
host: 192.168.205.1
redis:
host: 192.168.205.1
administrator:
username: admin
password: admin
node:
roleGroups:
default:
replicas: 1
worker:
roleGroups:
default:
replicas: 1
66 changes: 54 additions & 12 deletions internal/controller/cluster/cluster.go
Original file line number Diff line number Diff line change
@@ -5,55 +5,97 @@ import (

supersetv1alpha1 "github.com/zncdatadev/superset-operator/api/v1alpha1"
"github.com/zncdatadev/superset-operator/internal/controller/common"
"github.com/zncdatadev/superset-operator/internal/controller/node"
"github.com/zncdatadev/superset-operator/internal/controller/worker"
"github.com/zncdatadev/superset-operator/pkg/builder"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
"github.com/zncdatadev/superset-operator/pkg/util"
ctrl "sigs.k8s.io/controller-runtime"
)

var (
logger = ctrl.Log.WithName("controller").WithName("Cluster")
)

var _ reconciler.Reconciler = &Reconciler{}

type Reconciler struct {
reconciler.BaseClusterReconciler[*supersetv1alpha1.SupersetClusterSpec]
ClusterConfig *common.ClusterConfig
Options *builder.ClusterOptions
}

func (r *Reconciler) RegisterResources(_ context.Context) error {
r.AddResource(NewJobReconciler(r.Client, r.ClusterInfo, r.ClusterConfig))
r.AddResource(NewEnvSecretReconciler(r.Client, r.ClusterConfig))
r.AddResource(NewSupersetConfigSecretReconciler(r.Client, r.ClusterConfig))
func (r *Reconciler) RegisterResources(ctx context.Context) error {
r.AddResource(NewEnvSecretReconciler(r.Client, r.ClusterConfig, r.Options))
r.AddResource(NewSupersetConfigSecretReconciler(r.Client, r.ClusterConfig, r.Options))
r.AddResource(NewJobReconciler(r.Client, r.ClusterConfig, r.Options))

nodeReconciler := node.NewReconciler(
r.Client,
r.ClusterConfig,
&builder.RoleOptions{ClusterOptions: *r.Options, Name: "node"},
r.Spec.Node,
)
if err := nodeReconciler.RegisterResources(ctx); err != nil {
return err
}

r.AddResource(nodeReconciler)

workerReconciler := worker.NewReconciler(
r.Client,
r.ClusterConfig,
&builder.RoleOptions{ClusterOptions: *r.Options, Name: "worker"},
r.Spec.Worker,
)

if err := workerReconciler.RegisterResources(ctx); err != nil {
return err
}
r.AddResource(workerReconciler)

return nil
}

func NewReconciler(
client resourceClient.ResourceClient,
client *resourceClient.Client,
instance *supersetv1alpha1.SupersetCluster,
) *Reconciler {
image := util.Image{
image := &util.Image{
Custom: instance.Spec.Image.Custom,
Repo: instance.Spec.Image.Repo,
KDSVersion: instance.Spec.Image.KDSVersion,
ProductVersion: instance.Spec.Image.ProductVersion,
}

clusterInfo := &reconciler.ClusterInfo{
Name: instance.Name,
Namespace: instance.Namespace,
clusterOptions := &builder.ClusterOptions{
Name: instance.Name,
Namespace: instance.Namespace,
Labels: map[string]string{
"app.kubernetes.io/name": "hbase",
"app.kubernetes.io/managed-by": "hbase.zncdata.dev",
"app.kubernetes.io/instance": instance.Name,
},
Annotations: instance.GetAnnotations(),

ClusterOperation: instance.Spec.ClusterOperation,
Image: image,
}

clusterConfig := &common.ClusterConfig{
EnvSecretName: instance.Name + "env",
ConfigSecretName: instance.Name + "config",
EnvSecretName: instance.Name + "-env",
ConfigSecretName: instance.Name + "-config",
Spec: instance.Spec.ClusterConfig,
}

return &Reconciler{
BaseClusterReconciler: *reconciler.NewBaseClusterReconciler(
client,
clusterInfo,
clusterOptions,
&instance.Spec,
),
Options: clusterOptions,
ClusterConfig: clusterConfig,
}
}
66 changes: 35 additions & 31 deletions internal/controller/cluster/job.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import (
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
"github.com/zncdatadev/superset-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -20,16 +19,14 @@ type JobBuilder struct {
}

func NewJobBuilder(
client client.ResourceClient,
name string,
image util.Image,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *JobBuilder {
return &JobBuilder{
GenericJobBuilder: *builder.NewGenericJobBuilder(
client,
name,
image,
options,
),
ClusterConfig: clusterConfig,
}
@@ -41,28 +38,34 @@ func (b *JobBuilder) mainContainer() *corev1.Container {
MountPath: "/app/pythonpath",
ReadOnly: true,
}
container := builder.NewGenericContainerBuilder(
containerBuilder := builder.NewGenericContainerBuilder(
"superset-init",
b.Image.String(),
b.Image.PullPolicy,
).
AddVolumeMount(volumeMount).
SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; . /app/pythonpath/superset_init.sh"}).
AddEnvFromSecret(b.ClusterConfig.ConfigSecretName).
Build()
return container
b.Options.GetImage().String(),
b.Options.GetImage().PullPolicy,
)
containerBuilder.AddVolumeMount(volumeMount)
// SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; . /app/pythonpath/superset_init.sh"})
containerBuilder.SetCommand([]string{"tail", "-f"})
containerBuilder.AddEnvFromSecret(b.ClusterConfig.ConfigSecretName)
containerBuilder.AddEnvFromSecret(b.ClusterConfig.EnvSecretName)

existAdminSecretName := b.ClusterConfig.Spec.Administrator.ExistSecret
if existAdminSecretName != "" {
logger.Info("Using existing admin secret", "secret", existAdminSecretName, "namespace", b.Client.GetOwnerNamespace(), "name", b.Options.GetName())
containerBuilder.AddEnvFromSecret(existAdminSecretName)
}
return containerBuilder.Build()
}

func (b *JobBuilder) initContainer() *corev1.Container {
container := builder.NewGenericContainerBuilder(
"superset-init",
b.Image.String(),
b.Image.PullPolicy,
).
SetCommand([]string{"/bin/sh", "-c", "dockerize -wait \"tcp://$DB_HOST:$DB_PORT\" -timeout 120s"}).
AddEnvFromSecret(b.ClusterConfig.ConfigSecretName).
Build()
return container
containerBuilder := builder.NewGenericContainerBuilder(
"wait-for-postgres-redis",
"apache/superset:dockerize",
b.Options.GetImage().PullPolicy,
)
containerBuilder.SetCommand([]string{"/bin/sh", "-c", "dockerize -wait \"tcp://$DB_HOST:$DB_PORT\" -wait \"tcp://$REDIS_HOST:$REDIS_PORT\" -timeout 120s"})
containerBuilder.AddEnvFromSecret(b.ClusterConfig.EnvSecretName)
return containerBuilder.Build()
}

func (b *JobBuilder) GetVolumes() []corev1.Volume {
@@ -78,27 +81,28 @@ func (b *JobBuilder) GetVolumes() []corev1.Volume {
}
}

func (b *JobBuilder) Build(ctx context.Context) (k8sclient.Object, error) {
func (b *JobBuilder) Build(_ context.Context) (k8sclient.Object, error) {
b.AddContainer(*b.mainContainer())
b.AddInitContainer(*b.initContainer())
b.AddVolumes(b.GetVolumes())
b.SetRestPolicy(corev1.RestartPolicyNever)
b.SetName(b.Options.GetName() + "-init")
return b.GetObject(), nil
}

func NewJobReconciler(
client client.ResourceClient,
clusterInfo *reconciler.ClusterInfo,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *reconciler.SimpleResourceReconciler[builder.JobBuilder] {
name := "superset-init"
jobBuilder := NewJobBuilder(
client,
name,
clusterInfo.Image,
clusterConfig,
options,
)
return reconciler.NewSimpleResourceReconciler[builder.JobBuilder](
client,
name,
options,
jobBuilder,
)
}
166 changes: 140 additions & 26 deletions internal/controller/cluster/secret.go
Original file line number Diff line number Diff line change
@@ -2,15 +2,17 @@ package cluster

import (
"context"
"encoding/base64"
"errors"
"fmt"

"github.com/zncdatadev/superset-operator/internal/controller/common"
"github.com/zncdatadev/superset-operator/pkg/builder"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
"github.com/zncdatadev/superset-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -23,13 +25,14 @@ type EnvSecretBuilder struct {
}

func NewEnvSecretBuilder(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *EnvSecretBuilder {
return &EnvSecretBuilder{
SecretBuilder: *builder.NewSecretBuilder(
client,
clusterConfig.EnvSecretName,
options,
),
ClusterConfig: clusterConfig,
}
@@ -71,15 +74,15 @@ func (b *EnvSecretBuilder) getRedisConfig(ctx context.Context) (map[string]strin
if redisSpec.Host == "" {
return nil, fmt.Errorf(
"redis host is required, cluster: %s, namespace: %s",
b.Name,
b.Options.GetName(),
ns,
)
}

env["REDIS_HOST"] = redisSpec.Host
env["REDIS_PORT"] = fmt.Sprintf("%d", redisSpec.Port)
env["REDIS_DB"] = fmt.Sprintf("%d", redisSpec.DB)
env["REDIS_PROTOCOL"] = redisSpec.Proto
env["REDIS_PROTO"] = redisSpec.Proto

return env, nil
}
@@ -92,10 +95,10 @@ func (b *EnvSecretBuilder) getDBConfig(ctx context.Context) (map[string]string,

dbSpec := b.ClusterConfig.Spec.Database

var dbInline *resourceClient.DatabaseParams
var dbInline *client.DatabaseParams

if dbSpec.Inline != nil {
dbInline = resourceClient.NewDatabaseParams(
dbInline = client.NewDatabaseParams(
dbSpec.Inline.Driver,
dbSpec.Inline.Username,
dbSpec.Inline.Password,
@@ -105,10 +108,10 @@ func (b *EnvSecretBuilder) getDBConfig(ctx context.Context) (map[string]string,
)
}

dbConfig := resourceClient.DatabaseConfiguration{
dbConfig := client.DatabaseConfiguration{
Client: b.Client,
Context: ctx,
DbReference: &dbSpec.Reference,
DbReference: dbSpec.Reference,
DbInline: dbInline,
}

@@ -127,18 +130,127 @@ func (b *EnvSecretBuilder) getDBConfig(ctx context.Context) (map[string]string,
return env, nil
}

// GetAdminInfoFromSecret gets the admin info from the secret.
// If the secret is not set, it will return the admin info from the cluster config.
// If the secret is set, it will check the secret data.
func (b *EnvSecretBuilder) GetAdminInfoFromSecret(ctx context.Context) (map[string]string, error) {
adminSpec := b.ClusterConfig.Spec.Administrator
if adminSpec.ExistSecret == "" {
return map[string]string{
"ADMIN_USER": adminSpec.Username,
"ADMIN_FIRSTNAME": adminSpec.FirstName,
"ADMIN_LASTNAME": adminSpec.LastName,
"ADMIN_EMAIL": adminSpec.Email,
"ADMIN_PASSWORD": adminSpec.Password,
}, nil
}

// exist secret found, use first

secretObj := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: adminSpec.ExistSecret,
},
}

if err := b.Client.Client.Get(ctx, ctrlclient.ObjectKey{Namespace: b.Client.GetOwnerNamespace(), Name: adminSpec.ExistSecret}, secretObj); err != nil {
if apierrors.IsNotFound(err) {
return nil, fmt.Errorf("secret not found, secret: %s, namespace: %s", secretObj.Name, b.Client.GetOwnerNamespace())
} else {
return nil, err
}
}

if secretObj.Data == nil {
return nil, fmt.Errorf("secret data is empty, secret: %s, namespace: %s", secretObj.Name, b.Client.GetOwnerNamespace())
}

if _, ok := secretObj.Data["ADMIN_USER"]; !ok {
return nil, fmt.Errorf("username not found in secret: %s, namespace: %s", secretObj.Name, b.Client.GetOwnerNamespace())
}

if _, ok := secretObj.Data["ADMIN_PASSWORD"]; !ok {
return nil, fmt.Errorf("password not found in secret: %s, namespace: %s", secretObj.Name, b.Client.GetOwnerNamespace())
}

logger.V(1).Info("Get admin info from secret, and checkd, it will mount to container deriectly", "namespace", b.Client.GetOwnerNamespace(), "secret", secretObj.Name)
return nil, nil
}

// getFlaskSecretKey generates a secret key for flask app.
func (b *EnvSecretBuilder) getFlaskSecretKey() (map[string]string, error) {
var key string
appSecretKeySpec := b.ClusterConfig.Spec.AppSecretKey
if appSecretKeySpec == nil {
key = b.getRandomString(42)
} else if appSecretKeySpec.ExistSecret != "" {
secretObj := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: appSecretKeySpec.ExistSecret,
},
}
if err := b.Client.Get(context.Background(), secretObj); err != nil {
return nil, err
}
_, ok := secretObj.Data["SUPERSET_SECRET_KEY"]
if !ok {
return nil, fmt.Errorf("secret key not found in secret: %s, namespace: %s",
appSecretKeySpec.ExistSecret,
b.Client.GetOwnerNamespace(),
)
}
logger.V(1).Info("Get flask secret key from secret, and checked, it will mount to container deriectly", "namespace", b.Client.GetOwnerNamespace(), "secret", secretObj.Name)
return nil, nil
} else if appSecretKeySpec.SecretKey != "" {
key = appSecretKeySpec.SecretKey
} else {
key = b.getRandomString(42)
}

return map[string]string{
"SUPERSET_SECRET_KEY": key,
}, nil

}

// The secret key is generated by the owner reference UID.
// TODO: maybe we can use a more secure way to generate the secret key.
func (b *EnvSecretBuilder) getRandomString(length int) string {
uid := b.Client.GetOwnerReference().GetUID()
data := base64.StdEncoding.EncodeToString([]byte(uid))
if len(data) > length {
return data[:length]
}
return data
}

func (b *EnvSecretBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {

adminConfig, err := b.GetAdminInfoFromSecret(ctx)
if err != nil {
return nil, err
}
b.AddData(adminConfig)

redisConfig, err := b.getRedisConfig(ctx)
if err != nil {
return nil, err
}
b.AddData(redisConfig)

dbConfig, err := b.getDBConfig(ctx)
if err != nil {
return nil, err
}
b.AddData(redisConfig)
b.AddData(dbConfig)

flaskSecretKeyData, err := b.getFlaskSecretKey()
if err != nil {
return nil, err
}
b.AddData(flaskSecretKeyData)

b.SetName(b.ClusterConfig.EnvSecretName)
return b.GetObject(), nil
}

@@ -148,22 +260,19 @@ type SupersetConfigSecretBuilder struct {
}

func NewSupersetConfigSecretBuilder(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *SupersetConfigSecretBuilder {
return &SupersetConfigSecretBuilder{
SecretBuilder: *builder.NewSecretBuilder(
client,
clusterConfig.ConfigSecretName,
options,
),
ClusterConfig: clusterConfig,
}
}

func (b *SupersetConfigSecretBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {
return b.GetObject(), nil
}

func (b *SupersetConfigSecretBuilder) getConfig() string {
// Attention:
// Python intends to use 4 spaces per indentation level.
@@ -224,11 +333,11 @@ superset init
echo "Creating admin user..."
superset fab create-admin \
--username admin \
--firstname Superset \
--lastname Admin \
--email admin@superset.com \
--password admin \
--username ${ADMIN_USER} \
--firstname ${ADMIN_FIRSTNAME:-Superset} \
--lastname ${ADMIN_LASTNAME:-Admin} \
--email ${ADMIN_EMAIL:-admin@superset.com} \
--password ${ADMIN_PASSWORD} \
|| true
if [ -f "/app/configs/import_datasources.yaml" ]; then
@@ -250,47 +359,52 @@ if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid 0" > ~/bootstrap;
return util.IndentTab4Spaces(bootstrap)
}

func (b *SupersetConfigSecretBuilder) BuildConfig(_ context.Context) (ctrlclient.Object, error) {
func (b *SupersetConfigSecretBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
var config = map[string]string{
"superset_config.py": b.getConfig(),
"superset_init.sh": b.getSupersetInit(),
"superset_bootstrap.sh": b.getSupersetBootstrap(),
}
b.AddData(config)
b.SetName(b.ClusterConfig.ConfigSecretName)
return b.GetObject(), nil
}

func NewEnvSecretReconciler(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *reconciler.SimpleResourceReconciler[builder.ConfigBuilder] {

envSecretBuilder := NewEnvSecretBuilder(
client,
clusterConfig,
options,
)

return reconciler.NewSimpleResourceReconciler[builder.ConfigBuilder](
client,
clusterConfig.ConfigSecretName,
options,
envSecretBuilder,
)

}

func NewSupersetConfigSecretReconciler(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
options builder.Options,
) *reconciler.SimpleResourceReconciler[builder.ConfigBuilder] {

supersetConfigSecretBuilder := NewSupersetConfigSecretBuilder(
client,
clusterConfig,
options,
)

return reconciler.NewSimpleResourceReconciler[builder.ConfigBuilder](
client,
clusterConfig.ConfigSecretName,
options,
supersetConfigSecretBuilder,
)
}
46 changes: 19 additions & 27 deletions internal/controller/common/deployment.go
Original file line number Diff line number Diff line change
@@ -4,8 +4,7 @@ import (
"context"

"github.com/zncdatadev/superset-operator/pkg/builder"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
"github.com/zncdatadev/superset-operator/pkg/client"
corev1 "k8s.io/api/core/v1"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -14,52 +13,45 @@ var _ builder.Builder = &DeploymentBuilder{}

type DeploymentBuilder struct {
builder.GenericDeploymentBuilder
Ports []corev1.ContainerPort
ClusterConfig *ClusterConfig
}

func NewDeploymentBuilder(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *ClusterConfig,
roleGroupInfo *reconciler.RoleGroupInfo,
ports []corev1.ContainerPort,
envOverrides map[string]string,
commandOverrides []string,
options *builder.RoleGroupOptions,
) *DeploymentBuilder {
return &DeploymentBuilder{
GenericDeploymentBuilder: *builder.NewGenericDeploymentBuilder(
client,
roleGroupInfo.GetFullName(),
envOverrides,
commandOverrides,
roleGroupInfo.Image,
options,
),
Ports: ports,
ClusterConfig: clusterConfig,
}
}

func (b *DeploymentBuilder) GetMainContainer() builder.ContainerBuilder {
containerBuilder := builder.NewGenericContainerBuilder(
b.Name,
b.Image.String(),
b.Image.PullPolicy,
).AddEnvFromSecret(b.ClusterConfig.EnvSecretName).
SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; /usr/bin/run-server.sh"}).
AddVolumeMount(corev1.VolumeMount{Name: "superset-config", MountPath: "/app/superset", ReadOnly: true}).
AddPorts(b.Ports).
SetProbeWithHealth()
b.Options.Name,
b.Options.GetImage().String(),
b.Options.GetImage().PullPolicy,
)
containerBuilder.AddEnvFromSecret(b.ClusterConfig.EnvSecretName)
containerBuilder.SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; /usr/bin/run-server.sh"})
containerBuilder.AddVolumeMount(corev1.VolumeMount{Name: "superset-config", MountPath: "/app/superset", ReadOnly: true})
containerBuilder.AddPorts(b.Options.GetPorts())
containerBuilder.SetProbeWithHealth()
return containerBuilder
}

func (b *DeploymentBuilder) GetInitContainer() builder.ContainerBuilder {
containerBuilder := builder.NewGenericContainerBuilder(
"wait-for-postgres-redis",
b.Image.String(),
b.Image.PullPolicy,
).
SetCommand([]string{"/bin/sh", "-c", "dockerize -wait \"tcp://$DB_HOST:$DB_PORT\" -timeout 120s"}).
AddEnvFromSecret(b.ClusterConfig.EnvSecretName)
b.Options.GetImage().String(),
b.Options.GetImage().PullPolicy,
)
containerBuilder.SetCommand([]string{"/bin/sh", "-c", "dockerize -wait \"tcp://$DB_HOST:$DB_PORT\" -wait \"tcp://$REDIS_HOST:$REDIS_PORT\" -timeout 120s"})
containerBuilder.AddEnvFromSecret(b.ClusterConfig.EnvSecretName)
return containerBuilder
}

@@ -75,7 +67,7 @@ func (b *DeploymentBuilder) GetVolume() *corev1.Volume {
}

func (b *DeploymentBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {
b.AddContainer(b.GetInitContainer().Build())
b.AddContainer(b.GetMainContainer().Build())
b.AddInitContainer(b.GetInitContainer().Build())
b.AddVolume(b.GetVolume())

19 changes: 6 additions & 13 deletions internal/controller/node/deployment.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
package node

import (
supersetv1alpha1 "github.com/zncdatadev/superset-operator/api/v1alpha1"
"github.com/zncdatadev/superset-operator/internal/controller/common"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
corev1 "k8s.io/api/core/v1"
)

func NewDeploymentReconciler(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
roleGroupInfo *reconciler.RoleGroupInfo,
ports []corev1.ContainerPort,
spec *supersetv1alpha1.NodeRoleGroupSpec,
options *builder.RoleGroupOptions,
) *reconciler.DeploymentReconciler {
deploymentBuilder := common.NewDeploymentBuilder(
client,
clusterConfig,
roleGroupInfo,
ports,
spec.EnvOverrides,
spec.CommandOverrides,
options,
)

return reconciler.NewDeploymentReconciler(
client,
roleGroupInfo,
ports,
options,
deploymentBuilder,
)
}
20 changes: 9 additions & 11 deletions internal/controller/node/role.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

supersetv1alpha1 "github.com/zncdatadev/superset-operator/api/v1alpha1"
"github.com/zncdatadev/superset-operator/internal/controller/common"
"github.com/zncdatadev/superset-operator/pkg/builder"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
)
@@ -19,7 +20,7 @@ type Reconciler struct {
func (r *Reconciler) RegisterResources(ctx context.Context) error {
for name, rg := range r.Spec.RoleGroups {
mergedRoleGroup := rg.DeepCopy()
r.MergeRoleGroupSpec(&mergedRoleGroup)
r.MergeRoleGroupSpec(mergedRoleGroup)

if err := r.RegisterResourceWithRoleGroup(ctx, name, mergedRoleGroup); err != nil {
return err
@@ -34,8 +35,8 @@ func (r *Reconciler) RegisterResourceWithRoleGroup(
roleGroup *supersetv1alpha1.NodeRoleGroupSpec,
) error {

roleGroupInfo := &reconciler.RoleGroupInfo{
RoleInfo: *r.RoleInfo,
roleGroupOptions := &builder.RoleGroupOptions{
RoleOptions: *r.Options,
Name: name,
Replicas: roleGroup.Replicas,
PodDisruptionBudget: roleGroup.PodDisruptionBudget,
@@ -46,32 +47,29 @@ func (r *Reconciler) RegisterResourceWithRoleGroup(

service := reconciler.NewServiceReconciler(
r.Client,
roleGroupInfo.GetFullName(),
Ports,
roleGroupOptions,
)
r.AddResource(service)

deployment := NewDeploymentReconciler(
r.Client,
r.ClusterConfig,
roleGroupInfo,
Ports,
roleGroup,
roleGroupOptions,
)
r.AddResource(deployment)
return nil
}

func NewReconciler(
client resourceClient.ResourceClient,
roleInfo *reconciler.RoleInfo,
client *resourceClient.Client,
clusterConfig *common.ClusterConfig,
options *builder.RoleOptions,
spec *supersetv1alpha1.NodeSpec,
) *Reconciler {
return &Reconciler{
BaseRoleReconciler: *reconciler.NewBaseRoleReconciler(
client,
roleInfo,
options,
spec,
),
ClusterConfig: clusterConfig,
7 changes: 1 addition & 6 deletions internal/controller/supersetcluster_controller.go
Original file line number Diff line number Diff line change
@@ -58,14 +58,9 @@ func (r *SupersetClusterReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, err
}

resourceClient := resourceclient.ResourceClient{
resourceClient := &resourceclient.Client{
Client: r.Client,
OwnerReference: instance,
Labels: map[string]string{
"app.kubernetes.io/name": "hbase",
"app.kubernetes.io/managed-by": "hbase.zncdata.dev",
"app.kubernetes.io/instance": instance.Name,
},
}

clusterRreconciler := cluster.NewReconciler(resourceClient, instance)
64 changes: 26 additions & 38 deletions internal/controller/worker/deployment.go
Original file line number Diff line number Diff line change
@@ -3,86 +3,74 @@ package worker
import (
"context"

supersetv1alpha1 "github.com/zncdatadev/superset-operator/api/v1alpha1"
"github.com/zncdatadev/superset-operator/internal/controller/common"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

type DeploymentBuilder struct {
common.DeploymentBuilder
}

func NewDeploymentBuilder(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
roleGroupInfo *reconciler.RoleGroupInfo,
ports []corev1.ContainerPort,
envOverrides map[string]string,
commandOverrides []string,
options *builder.RoleGroupOptions,
) *DeploymentBuilder {
deploymentBuilder := common.NewDeploymentBuilder(
client,
clusterConfig,
roleGroupInfo,
ports,
envOverrides,
commandOverrides,
options,
)
return &DeploymentBuilder{
DeploymentBuilder: *deploymentBuilder,
}
}

func (b *DeploymentBuilder) Build(ctx context.Context) (client.Object, error) {
func (b *DeploymentBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {
_, err := b.DeploymentBuilder.Build(ctx)
if err != nil {
return nil, err
}

mainContainer := b.DeploymentBuilder.GetMainContainer().
SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker"}).
SetLiveProbe(&corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
Exec: &corev1.ExecAction{
Command: []string{"sh", "-c", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$HOSTNAME"},
},
mainContainerBuilder := b.DeploymentBuilder.GetMainContainer()
mainContainerBuilder.SetCommand([]string{"/bin/sh", "-c", ". /app/pythonpath/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker"})
mainContainerBuilder.SetLiveProbe(&corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
Exec: &corev1.ExecAction{
Command: []string{"sh", "-c", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$HOSTNAME"},
},
FailureThreshold: 3,
InitialDelaySeconds: 120,
PeriodSeconds: 60,
SuccessThreshold: 1,
TimeoutSeconds: 60,
}).
Build()
},
FailureThreshold: 3,
InitialDelaySeconds: 120,
PeriodSeconds: 60,
SuccessThreshold: 1,
TimeoutSeconds: 60,
})
mainContainer := mainContainerBuilder.Build()

b.ResetContainers([]corev1.Container{*mainContainer})

return b.DeploymentBuilder.Build(ctx)
return b.GetObject(), nil
}

func NewDeploymentReconciler(
client resourceClient.ResourceClient,
client *client.Client,
clusterConfig *common.ClusterConfig,
roleGroupInfo *reconciler.RoleGroupInfo,
ports []corev1.ContainerPort,
spec *supersetv1alpha1.WorkerRoleGroupSpec,
options *builder.RoleGroupOptions,
) *reconciler.DeploymentReconciler {
deploymentBuilder := NewDeploymentBuilder(
client,
clusterConfig,
roleGroupInfo,
ports,
spec.EnvOverrides,
spec.CommandOverrides,
options,
)

return reconciler.NewDeploymentReconciler(
client,
roleGroupInfo,
ports,
options,
deploymentBuilder,
)
}
20 changes: 9 additions & 11 deletions internal/controller/worker/role.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

supersetv1alpha1 "github.com/zncdatadev/superset-operator/api/v1alpha1"
"github.com/zncdatadev/superset-operator/internal/controller/common"
"github.com/zncdatadev/superset-operator/pkg/builder"
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/reconciler"
)
@@ -19,7 +20,7 @@ type Reconciler struct {
func (r *Reconciler) RegisterResources(ctx context.Context) error {
for name, rg := range r.Spec.RoleGroups {
mergedRoleGroup := rg.DeepCopy()
r.MergeRoleGroupSpec(&mergedRoleGroup)
r.MergeRoleGroupSpec(mergedRoleGroup)

if err := r.RegisterResourceWithRoleGroup(ctx, name, mergedRoleGroup); err != nil {
return err
@@ -34,8 +35,8 @@ func (r *Reconciler) RegisterResourceWithRoleGroup(
roleGroup *supersetv1alpha1.WorkerRoleGroupSpec,
) error {

roleGroupInfo := &reconciler.RoleGroupInfo{
RoleInfo: *r.RoleInfo,
roleGroupOptions := &builder.RoleGroupOptions{
RoleOptions: *r.Options,
Name: name,
Replicas: roleGroup.Replicas,
PodDisruptionBudget: roleGroup.PodDisruptionBudget,
@@ -46,33 +47,30 @@ func (r *Reconciler) RegisterResourceWithRoleGroup(

service := reconciler.NewServiceReconciler(
r.Client,
roleGroupInfo.GetFullName(),
Ports,
roleGroupOptions,
)
r.AddResource(service)

deployment := NewDeploymentReconciler(
r.Client,
r.ClusterConfig,
roleGroupInfo,
Ports,
roleGroup,
roleGroupOptions,
)
r.AddResource(deployment)

return nil
}

func NewReconciler(
client resourceClient.ResourceClient,
roleInfo *reconciler.RoleInfo,
client *resourceClient.Client,
clusterConfig *common.ClusterConfig,
options *builder.RoleOptions,
spec *supersetv1alpha1.WorkerSpec,
) *Reconciler {
return &Reconciler{
BaseRoleReconciler: *reconciler.NewBaseRoleReconciler(
client,
roleInfo,
options,
spec,
),
ClusterConfig: clusterConfig,
4 changes: 2 additions & 2 deletions pkg/apis/v1alpha1/zz_generated.deepcopy.go
33 changes: 21 additions & 12 deletions pkg/builder/builder.go
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ package builder
import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/client"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -16,41 +16,50 @@ var (
type Builder interface {
Build(ctx context.Context) (ctrlclient.Object, error)
GetObjectMeta() metav1.ObjectMeta
GetClient() resourceClient.ResourceClient
GetClient() *client.Client
SetName(name string)
GetName() string
}

var _ Builder = &BaseResourceBuilder{}

type BaseResourceBuilder struct {
Client resourceClient.ResourceClient
Client *client.Client
Options Options

Name string
modifiedName string
}

func (b *BaseResourceBuilder) GetClient() resourceClient.ResourceClient {
func (b *BaseResourceBuilder) GetClient() *client.Client {
return b.Client
}

func (b *BaseResourceBuilder) SetName(name string) {
b.modifiedName = name
}

func (b *BaseResourceBuilder) GetName() string {
return b.Name
if b.modifiedName != "" {
return b.modifiedName
}
return b.Options.GetFullName()
}

func (b *BaseResourceBuilder) GetObjectMeta() metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: b.Name,
Name: b.GetName(),
Namespace: b.Client.GetOwnerNamespace(),
Labels: b.Client.GetLabels(),
Annotations: b.Client.GetAnnotations(),
Labels: b.Options.GetLabels(),
Annotations: b.Options.GetAnnotations(),
}
}

// GetObjectMetaWithClusterScope returns the object meta with cluster scope
func (b *BaseResourceBuilder) GetObjectMetaWithClusterScope() metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: b.Name,
Labels: b.Client.GetLabels(),
Annotations: b.Client.GetAnnotations(),
Name: b.GetName(),
Labels: b.Options.GetLabels(),
Annotations: b.Options.GetAnnotations(),
}
}

28 changes: 14 additions & 14 deletions pkg/builder/config.go
Original file line number Diff line number Diff line change
@@ -3,9 +3,9 @@ package builder
import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/client"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

type ConfigBuilder interface {
@@ -27,13 +27,13 @@ type BaseConfigBuilder struct {
}

func NewBaseConfigBuilder(
client resourceClient.ResourceClient,
name string,
client *client.Client,
options Options,
) *BaseConfigBuilder {
return &BaseConfigBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
data: make(map[string]string),
}
@@ -82,11 +82,11 @@ type ConfigMapBuilder struct {
}

func NewConfigMapBuilder(
client resourceClient.ResourceClient,
name string,
client *client.Client,
options Options,
) *ConfigMapBuilder {
return &ConfigMapBuilder{
BaseConfigBuilder: *NewBaseConfigBuilder(client, name),
BaseConfigBuilder: *NewBaseConfigBuilder(client, options),
}
}

@@ -97,7 +97,7 @@ func (b *ConfigMapBuilder) GetObject() *corev1.ConfigMap {
}
}

func (b *ConfigMapBuilder) Build(_ context.Context) (client.Object, error) {
func (b *ConfigMapBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
return b.GetObject(), nil
}

@@ -108,11 +108,11 @@ type SecretBuilder struct {
var _ ConfigBuilder = &SecretBuilder{}

func NewSecretBuilder(
client resourceClient.ResourceClient,
name string,
client *client.Client,
options Options,
) *SecretBuilder {
return &SecretBuilder{
BaseConfigBuilder: *NewBaseConfigBuilder(client, name),
BaseConfigBuilder: *NewBaseConfigBuilder(client, options),
}
}

@@ -123,6 +123,6 @@ func (b *SecretBuilder) GetObject() *corev1.Secret {
}
}

func (b *SecretBuilder) Build(_ context.Context) (client.Object, error) {
func (b *SecretBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
return b.GetObject(), nil
}
153 changes: 75 additions & 78 deletions pkg/builder/container.go
Original file line number Diff line number Diff line change
@@ -16,46 +16,46 @@ var (
type ContainerBuilder interface {
Build() *corev1.Container

AddVolumeMounts(mounts []corev1.VolumeMount) ContainerBuilder
AddVolumeMount(mount corev1.VolumeMount) ContainerBuilder
ResetVolumeMounts(mounts []corev1.VolumeMount) ContainerBuilder
AddVolumeMounts(mounts []corev1.VolumeMount)
AddVolumeMount(mount corev1.VolumeMount)
ResetVolumeMounts(mounts []corev1.VolumeMount)
GetVolumeMounts() []corev1.VolumeMount

AddEnvVars(envVars []corev1.EnvVar) ContainerBuilder
ResetEnvVars(envVars []corev1.EnvVar) ContainerBuilder
AddEnvVars(envVars []corev1.EnvVar)
ResetEnvVars(envVars []corev1.EnvVar)
GetEnvVars() []corev1.EnvVar

AddEnvs(envs map[string]string) ContainerBuilder
AddEnv(key, value string) ContainerBuilder
AddEnvs(envs map[string]string)
AddEnv(key, value string)

AddEnvFrom(envs []corev1.EnvFromSource) ContainerBuilder
AddEnvFromSecret(secretName string) ContainerBuilder
AddEnvFromConfigMap(configMapName string) ContainerBuilder
ResetEnvFrom(envs []corev1.EnvFromSource) ContainerBuilder
AddEnvFrom(envs []corev1.EnvFromSource)
AddEnvFromSecret(secretName string)
AddEnvFromConfigMap(configMapName string)
ResetEnvFrom(envs []corev1.EnvFromSource)
GetEnvFrom() []corev1.EnvFromSource

AddPorts(ports []corev1.ContainerPort) ContainerBuilder
AddPort(port corev1.ContainerPort) ContainerBuilder
ResetPorts(ports []corev1.ContainerPort) ContainerBuilder
AddPorts(ports []corev1.ContainerPort)
AddPort(port corev1.ContainerPort)
ResetPorts(ports []corev1.ContainerPort)
GetPorts() []corev1.ContainerPort

SetResources(resources apiv1alpha1.ResourcesSpec) ContainerBuilder
SetResources(resources apiv1alpha1.ResourcesSpec)

SetLiveProbe(probe *corev1.Probe) ContainerBuilder
SetReadinessProbe(probe *corev1.Probe) ContainerBuilder
SetStartupProbe(probe *corev1.Probe) ContainerBuilder
SetLiveProbe(probe *corev1.Probe)
SetReadinessProbe(probe *corev1.Probe)
SetStartupProbe(probe *corev1.Probe)

SetSecurityContext(user int64, group int64, nonRoot bool) ContainerBuilder
SetSecurityContext(user int64, group int64, nonRoot bool)
// SetCommand sets the command for the container and clears the args.
SetCommand(command []string) ContainerBuilder
SetArgs(args []string) ContainerBuilder
SetCommand(command []string)
SetArgs(args []string)

OverrideEnv(envs map[string]string) ContainerBuilder
OverrideEnv(envs map[string]string)
// OverrideCommand sets the command for the container and clears the command.
OverrideCommand(command []string) ContainerBuilder
OverrideCommand(command []string)

AutomaticSetProbe() ContainerBuilder
SetProbeWithHealth() ContainerBuilder
AutomaticSetProbe()
SetProbeWithHealth()
}

var _ ContainerBuilder = &GenericContainerBuilder{}
@@ -95,27 +95,27 @@ func (b *GenericContainerBuilder) Build() *corev1.Container {
return obj
}

func (b *GenericContainerBuilder) AddVolumeMounts(mounts []corev1.VolumeMount) ContainerBuilder {
func (b *GenericContainerBuilder) AddVolumeMounts(mounts []corev1.VolumeMount) {
v := b.getObject().VolumeMounts
v = append(v, mounts...)
b.getObject().VolumeMounts = v
return b

}

func (b *GenericContainerBuilder) AddVolumeMount(mount corev1.VolumeMount) ContainerBuilder {
return b.AddVolumeMounts([]corev1.VolumeMount{mount})
func (b *GenericContainerBuilder) AddVolumeMount(mount corev1.VolumeMount) {
b.AddVolumeMounts([]corev1.VolumeMount{mount})
}

func (b *GenericContainerBuilder) ResetVolumeMounts(mounts []corev1.VolumeMount) ContainerBuilder {
func (b *GenericContainerBuilder) ResetVolumeMounts(mounts []corev1.VolumeMount) {
b.getObject().VolumeMounts = mounts
return b

}

func (b *GenericContainerBuilder) GetVolumeMounts() []corev1.VolumeMount {
return b.getObject().VolumeMounts
}

func (b *GenericContainerBuilder) AddEnvVars(envVars []corev1.EnvVar) ContainerBuilder {
func (b *GenericContainerBuilder) AddEnvVars(envVars []corev1.EnvVar) {
envs := b.getObject().Env
envs = append(envs, envVars...)
var envNames []string
@@ -126,46 +126,46 @@ func (b *GenericContainerBuilder) AddEnvVars(envVars []corev1.EnvVar) ContainerB
envNames = append(envNames, env.Name)
}
b.getObject().Env = envs
return b

}

func (b *GenericContainerBuilder) AddEnvVar(env corev1.EnvVar) ContainerBuilder {
return b.AddEnvVars([]corev1.EnvVar{env})
func (b *GenericContainerBuilder) AddEnvVar(env corev1.EnvVar) {
b.AddEnvVars([]corev1.EnvVar{env})
}

func (b *GenericContainerBuilder) ResetEnvVars(envVars []corev1.EnvVar) ContainerBuilder {
func (b *GenericContainerBuilder) ResetEnvVars(envVars []corev1.EnvVar) {
b.getObject().Env = envVars
return b

}

func (b *GenericContainerBuilder) GetEnvVars() []corev1.EnvVar {
return b.getObject().Env
}

func (b *GenericContainerBuilder) AddEnvs(envs map[string]string) ContainerBuilder {
func (b *GenericContainerBuilder) AddEnvs(envs map[string]string) {
var envVars []corev1.EnvVar
for name, value := range envs {
envVars = append(envVars, corev1.EnvVar{
Name: name,
Value: value,
})
}
return b.AddEnvVars(envVars)
b.AddEnvVars(envVars)
}

func (b *GenericContainerBuilder) AddEnv(key, value string) ContainerBuilder {
return b.AddEnvs(map[string]string{key: value})
func (b *GenericContainerBuilder) AddEnv(key, value string) {
b.AddEnvs(map[string]string{key: value})
}

func (b *GenericContainerBuilder) AddEnvFrom(envs []corev1.EnvFromSource) ContainerBuilder {
func (b *GenericContainerBuilder) AddEnvFrom(envs []corev1.EnvFromSource) {
e := b.getObject().EnvFrom
e = append(e, envs...)
b.getObject().EnvFrom = e
return b

}

func (b *GenericContainerBuilder) AddEnvFromSecret(secretName string) ContainerBuilder {
return b.AddEnvFrom([]corev1.EnvFromSource{
func (b *GenericContainerBuilder) AddEnvFromSecret(secretName string) {
b.AddEnvFrom([]corev1.EnvFromSource{
{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
@@ -176,8 +176,8 @@ func (b *GenericContainerBuilder) AddEnvFromSecret(secretName string) ContainerB
})
}

func (b *GenericContainerBuilder) AddEnvFromConfigMap(configMapName string) ContainerBuilder {
return b.AddEnvFrom([]corev1.EnvFromSource{
func (b *GenericContainerBuilder) AddEnvFromConfigMap(configMapName string) {
b.AddEnvFrom([]corev1.EnvFromSource{
{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
@@ -188,57 +188,57 @@ func (b *GenericContainerBuilder) AddEnvFromConfigMap(configMapName string) Cont
})
}

func (b *GenericContainerBuilder) ResetEnvFrom(envs []corev1.EnvFromSource) ContainerBuilder {
func (b *GenericContainerBuilder) ResetEnvFrom(envs []corev1.EnvFromSource) {
b.getObject().EnvFrom = envs
return b

}

func (b *GenericContainerBuilder) GetEnvFrom() []corev1.EnvFromSource {
return b.getObject().EnvFrom
}

func (b *GenericContainerBuilder) AddPorts(ports []corev1.ContainerPort) ContainerBuilder {
func (b *GenericContainerBuilder) AddPorts(ports []corev1.ContainerPort) {
p := b.getObject().Ports
p = append(p, ports...)
b.getObject().Ports = p
return b

}

func (b *GenericContainerBuilder) AddPort(port corev1.ContainerPort) ContainerBuilder {
return b.AddPorts([]corev1.ContainerPort{port})
func (b *GenericContainerBuilder) AddPort(port corev1.ContainerPort) {
b.AddPorts([]corev1.ContainerPort{port})
}

func (b *GenericContainerBuilder) ResetPorts(ports []corev1.ContainerPort) ContainerBuilder {
func (b *GenericContainerBuilder) ResetPorts(ports []corev1.ContainerPort) {
b.getObject().Ports = ports
return b

}

func (b *GenericContainerBuilder) GetPorts() []corev1.ContainerPort {
return b.getObject().Ports
}

func (b *GenericContainerBuilder) SetCommand(command []string) ContainerBuilder {
func (b *GenericContainerBuilder) SetCommand(command []string) {
b.getObject().Command = command
b.getObject().Args = []string{}
return b

}

func (b *GenericContainerBuilder) SetArgs(args []string) ContainerBuilder {
func (b *GenericContainerBuilder) SetArgs(args []string) {
b.getObject().Args = args
return b

}

func (b *GenericContainerBuilder) OverrideEnv(envs map[string]string) ContainerBuilder {
func (b *GenericContainerBuilder) OverrideEnv(envs map[string]string) {
b.getObject().Env = []corev1.EnvVar{}
return b.AddEnvs(envs)
b.AddEnvs(envs)
}

func (b *GenericContainerBuilder) OverrideCommand(command []string) ContainerBuilder {
func (b *GenericContainerBuilder) OverrideCommand(command []string) {
b.getObject().Command = []string{}
return b.SetCommand(command)
b.SetCommand(command)
}

func (b *GenericContainerBuilder) SetResources(resources apiv1alpha1.ResourcesSpec) ContainerBuilder {
func (b *GenericContainerBuilder) SetResources(resources apiv1alpha1.ResourcesSpec) {
obj := b.getObject()
if resources.CPU != nil {
obj.Resources.Requests[corev1.ResourceCPU] = resources.CPU.Min
@@ -247,32 +247,31 @@ func (b *GenericContainerBuilder) SetResources(resources apiv1alpha1.ResourcesSp
if resources.Memory != nil {
obj.Resources.Requests[corev1.ResourceMemory] = resources.Memory.Limit
}
return b

}

func (b *GenericContainerBuilder) SetLiveProbe(probe *corev1.Probe) ContainerBuilder {
func (b *GenericContainerBuilder) SetLiveProbe(probe *corev1.Probe) {
b.getObject().LivenessProbe = probe
return b

}

func (b *GenericContainerBuilder) SetReadinessProbe(probe *corev1.Probe) ContainerBuilder {
func (b *GenericContainerBuilder) SetReadinessProbe(probe *corev1.Probe) {
b.getObject().ReadinessProbe = probe
return b

}

func (b *GenericContainerBuilder) SetStartupProbe(probe *corev1.Probe) ContainerBuilder {
func (b *GenericContainerBuilder) SetStartupProbe(probe *corev1.Probe) {
b.getObject().StartupProbe = probe
return b

}

func (b *GenericContainerBuilder) SetSecurityContext(user int64, group int64, nonRoot bool) ContainerBuilder {
func (b *GenericContainerBuilder) SetSecurityContext(user int64, group int64, nonRoot bool) {
b.getObject().SecurityContext = &corev1.SecurityContext{
RunAsUser: &user,
RunAsGroup: &group,
AllowPrivilegeEscalation: &nonRoot,
}
return b

}

// AutomaticSetProbe sets the liveness, readiness and startup probes
@@ -300,13 +299,13 @@ func (b *GenericContainerBuilder) SetSecurityContext(user int64, group int64, no
// - periodSeconds: 10
// - successThreshold: 1
// - timeoutSeconds: 3
func (b *GenericContainerBuilder) AutomaticSetProbe() ContainerBuilder {
func (b *GenericContainerBuilder) AutomaticSetProbe() {

probeHandler := b.getProbeHandler()

if probeHandler == nil {
logger.V(2).Info("No probe handler found, skip setting probes")
return b
return
}

// Set startup probe
@@ -340,7 +339,6 @@ func (b *GenericContainerBuilder) AutomaticSetProbe() ContainerBuilder {
}
b.SetReadinessProbe(readinessProbe)

return b
}

// getProbeHandler returns the handler for the probe
@@ -370,7 +368,7 @@ func (b *GenericContainerBuilder) getProbeHandler() *corev1.ProbeHandler {
return nil
}

func (b *GenericContainerBuilder) SetProbeWithHealth() ContainerBuilder {
func (b *GenericContainerBuilder) SetProbeWithHealth() {
ok := false
for _, port := range b.getObject().Ports {
if port.Name == "http" {
@@ -420,5 +418,4 @@ func (b *GenericContainerBuilder) SetProbeWithHealth() ContainerBuilder {
logger.V(2).Info("No http port found, skip setting probes")
}

return b
}
183 changes: 94 additions & 89 deletions pkg/builder/deployment.go
Original file line number Diff line number Diff line change
@@ -1,149 +1,154 @@
package builder

import (
resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"context"

client "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/util"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var (
DefaultReplicas = int32(1)
)

type DeploymentBuilder interface {
Builder

GetObject() *appv1.Deployment
SetReplicas(replicas *int32) DeploymentBuilder
SetReplicas(replicas *int32)

AddContainer(*corev1.Container) DeploymentBuilder
AddContainers([]corev1.Container) DeploymentBuilder
ResetContainers(containers []corev1.Container) DeploymentBuilder
AddContainer(*corev1.Container)
AddContainers([]corev1.Container)
ResetContainers(containers []corev1.Container)

AddInitContainer(*corev1.Container) DeploymentBuilder
AddInitContainers([]corev1.Container) DeploymentBuilder
ResetInitContainers(containers []corev1.Container) DeploymentBuilder
AddInitContainer(*corev1.Container)
AddInitContainers([]corev1.Container)
ResetInitContainers(containers []corev1.Container)

AddVolume(*corev1.Volume) DeploymentBuilder
AddVolumes([]corev1.Volume) DeploymentBuilder
ResetVolumes(volumes []corev1.Volume) DeploymentBuilder
AddVolume(*corev1.Volume)
AddVolumes([]corev1.Volume)
ResetVolumes(volumes []corev1.Volume)

AddTerminationGracePeriodSeconds(seconds *int64) DeploymentBuilder
AddAffinity(*corev1.Affinity) DeploymentBuilder
AddTerminationGracePeriodSeconds(seconds *int64)
AddAffinity(*corev1.Affinity)
}

var _ DeploymentBuilder = &GenericDeploymentBuilder{}

type GenericDeploymentBuilder struct {
BaseResourceBuilder
EnvOverrides map[string]string
CommandOverrides []string
Image util.Image
Options *RoleGroupOptions

obj *appv1.Deployment
replicas *int32
initContainers []corev1.Container
containers []corev1.Container
volumes []corev1.Volume
terminationGracePeriodSeconds *int64
affinity *corev1.Affinity
}

func NewGenericDeploymentBuilder(
client resourceClient.ResourceClient,
name string,
envOverrides map[string]string,
commandOverrides []string,
image util.Image,
client *client.Client,
options *RoleGroupOptions,
) *GenericDeploymentBuilder {
return &GenericDeploymentBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
EnvOverrides: envOverrides,
CommandOverrides: commandOverrides,
Image: image,
Options: options,
}
}

func (b *GenericDeploymentBuilder) GetObject() *appv1.Deployment {
if b.obj == nil {
b.obj = &appv1.Deployment{
ObjectMeta: b.GetObjectMeta(),
Spec: appv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: b.Client.GetLabels(),
if b.replicas == nil {
b.replicas = &DefaultReplicas
}
obj := &appv1.Deployment{
Spec: appv1.DeploymentSpec{
Replicas: b.replicas,
Selector: &metav1.LabelSelector{
MatchLabels: b.Options.GetMatchingLabels(),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Options.GetLabels(),
Annotations: b.Options.GetAnnotations(),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Client.GetLabels(),
Annotations: b.Client.GetAnnotations(),
},
Spec: corev1.PodSpec{
InitContainers: b.initContainers,
Containers: b.containers,
Volumes: b.volumes,
Affinity: b.affinity,
TerminationGracePeriodSeconds: b.terminationGracePeriodSeconds,
},
},
}
},
}
return b.obj
return obj
}

func (b *GenericDeploymentBuilder) SetReplicas(replicas *int32) DeploymentBuilder {
b.GetObject().Spec.Replicas = replicas
return b
func (b *GenericDeploymentBuilder) SetReplicas(replicas *int32) {
b.replicas = replicas
}

func (b *GenericDeploymentBuilder) AddContainer(container *corev1.Container) DeploymentBuilder {
c := b.GetObject().Spec.Template.Spec.Containers
c = append(c, *container)
b.GetObject().Spec.Template.Spec.Containers = c
return b
func (b *GenericDeploymentBuilder) AddContainer(container *corev1.Container) {
b.containers = append(b.containers, *container)
}

func (b *GenericDeploymentBuilder) AddContainers(containers []corev1.Container) DeploymentBuilder {
c := b.GetObject().Spec.Template.Spec.Containers
c = append(c, containers...)
b.GetObject().Spec.Template.Spec.Containers = c
return b
func (b *GenericDeploymentBuilder) AddContainers(containers []corev1.Container) {
b.containers = append(b.containers, containers...)
}

func (b *GenericDeploymentBuilder) ResetContainers(containers []corev1.Container) DeploymentBuilder {
b.GetObject().Spec.Template.Spec.Containers = containers
return b
func (b *GenericDeploymentBuilder) ResetContainers(containers []corev1.Container) {
b.containers = containers
}
func (b *GenericDeploymentBuilder) AddInitContainers(containers []corev1.Container) DeploymentBuilder {
c := b.GetObject().Spec.Template.Spec.InitContainers
c = append(c, containers...)
b.GetObject().Spec.Template.Spec.InitContainers = c
return b
func (b *GenericDeploymentBuilder) AddInitContainers(containers []corev1.Container) {
b.initContainers = append(b.initContainers, containers...)
}

func (b *GenericDeploymentBuilder) ResetInitContainers(containers []corev1.Container) DeploymentBuilder {
b.GetObject().Spec.Template.Spec.InitContainers = containers
return b
func (b *GenericDeploymentBuilder) AddInitContainer(container *corev1.Container) {
b.initContainers = append(b.initContainers, *container)
}

func (b *GenericDeploymentBuilder) AddInitContainer(container *corev1.Container) DeploymentBuilder {
c := b.GetObject().Spec.Template.Spec.InitContainers
c = append(c, *container)
b.GetObject().Spec.Template.Spec.InitContainers = c
return b
func (b *GenericDeploymentBuilder) ResetInitContainers(containers []corev1.Container) {
b.initContainers = containers
}

func (b *GenericDeploymentBuilder) AddVolume(volume *corev1.Volume) DeploymentBuilder {
v := b.GetObject().Spec.Template.Spec.Volumes
v = append(v, *volume)
b.GetObject().Spec.Template.Spec.Volumes = v
return b
func (b *GenericDeploymentBuilder) AddVolume(volume *corev1.Volume) {
b.volumes = append(b.volumes, *volume)
}
func (b *GenericDeploymentBuilder) AddVolumes(volumes []corev1.Volume) {
b.volumes = append(b.volumes, volumes...)
}
func (b *GenericDeploymentBuilder) AddVolumes(volumes []corev1.Volume) DeploymentBuilder {
v := b.GetObject().Spec.Template.Spec.Volumes
v = append(v, volumes...)
b.GetObject().Spec.Template.Spec.Volumes = v
return b

func (b *GenericDeploymentBuilder) ResetVolumes(volumes []corev1.Volume) {
b.volumes = volumes
}

func (b *GenericDeploymentBuilder) ResetVolumes(volumes []corev1.Volume) DeploymentBuilder {
b.GetObject().Spec.Template.Spec.Volumes = volumes
return b
func (b *GenericDeploymentBuilder) AddTerminationGracePeriodSeconds(seconds *int64) {
b.terminationGracePeriodSeconds = seconds
}

func (b *GenericDeploymentBuilder) AddTerminationGracePeriodSeconds(seconds *int64) DeploymentBuilder {
b.GetObject().Spec.Template.Spec.TerminationGracePeriodSeconds = seconds
return b
func (b *GenericDeploymentBuilder) AddAffinity(affinity *corev1.Affinity) {
b.affinity = affinity
}

func (b *GenericDeploymentBuilder) AddAffinity(affinity *corev1.Affinity) DeploymentBuilder {
b.GetObject().Spec.Template.Spec.Affinity = affinity
return b
func (b *GenericDeploymentBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
obj := b.GetObject()

if b.containers == nil {
obj.Spec.Template.Spec.Containers = []corev1.Container{
{
Name: b.Options.Name,
Image: b.Options.GetImage().String(),
Env: util.EnvsToEnvVars(b.Options.EnvOverrides),
Command: b.Options.CommandOverrides,
},
}
}
return obj, nil
}
128 changes: 63 additions & 65 deletions pkg/builder/job.go
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/util"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -14,121 +13,120 @@ import (
type JobBuilder interface {
Builder
GetObject() *batchv1.Job
AddContainers([]corev1.Container) JobBuilder
AddContainer(corev1.Container) JobBuilder
ResetContainers([]corev1.Container) JobBuilder

AddContainers([]corev1.Container)
AddContainer(corev1.Container)
ResetContainers([]corev1.Container)
GetContainers() []corev1.Container
AddInitContainers([]corev1.Container) JobBuilder
AddInitContainer(corev1.Container) JobBuilder
ResetInitContainers([]corev1.Container) JobBuilder

AddInitContainers([]corev1.Container)
AddInitContainer(corev1.Container)
ResetInitContainers([]corev1.Container)
GetInitContainers() []corev1.Container
AddVolumes([]corev1.Volume) JobBuilder
AddVolume(corev1.Volume) JobBuilder
ResetVolumes([]corev1.Volume) JobBuilder

AddVolumes([]corev1.Volume)
AddVolume(corev1.Volume)
ResetVolumes([]corev1.Volume)
GetVolumes() []corev1.Volume

SetRestPolicy(corev1.RestartPolicy)
}

type GenericJobBuilder struct {
BaseResourceBuilder
Image util.Image

obj *batchv1.Job
containers []corev1.Container
initContainers []corev1.Container
volumes []corev1.Volume
resetPolicy corev1.RestartPolicy
}

func NewGenericJobBuilder(
client resourceClient.ResourceClient,
name string,
image util.Image,
client *resourceClient.Client,
options Options,
) *GenericJobBuilder {
return &GenericJobBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
Image: image,
}
}

func (b *GenericJobBuilder) GetObject() *batchv1.Job {
if b.obj == nil {
b.obj = &batchv1.Job{
ObjectMeta: b.GetObjectMeta(),
Spec: batchv1.JobSpec{
Selector: &metav1.LabelSelector{
MatchLabels: b.Client.GetLabels(),
obj := &batchv1.Job{
ObjectMeta: b.GetObjectMeta(),
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Options.GetLabels(),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Client.GetLabels(),
Annotations: b.Client.GetAnnotations(),
},
Spec: corev1.PodSpec{
InitContainers: b.initContainers,
Containers: b.containers,
Volumes: b.volumes,
RestartPolicy: b.resetPolicy,
},
},
}
},
}
return b.obj
return obj
}

func (b *GenericJobBuilder) AddContainers(containers []corev1.Container) JobBuilder {
c := b.GetObject().Spec.Template.Spec.Containers
c = append(c, containers...)
b.GetObject().Spec.Template.Spec.Containers = c
return b
func (b *GenericJobBuilder) AddContainers(containers []corev1.Container) {
b.containers = append(b.containers, containers...)
}

func (b *GenericJobBuilder) AddContainer(container corev1.Container) JobBuilder {
return b.AddContainers([]corev1.Container{container})
func (b *GenericJobBuilder) AddContainer(container corev1.Container) {
b.AddContainers([]corev1.Container{container})
}

func (b *GenericJobBuilder) ResetContainers(containers []corev1.Container) JobBuilder {
b.GetObject().Spec.Template.Spec.Containers = containers
return b
func (b *GenericJobBuilder) ResetContainers(containers []corev1.Container) {
b.containers = containers
}

func (b *GenericJobBuilder) GetContainers() []corev1.Container {
return b.GetObject().Spec.Template.Spec.Containers
return b.containers
}

func (b *GenericJobBuilder) AddInitContainers(containers []corev1.Container) JobBuilder {
c := b.GetObject().Spec.Template.Spec.InitContainers
c = append(c, containers...)
b.GetObject().Spec.Template.Spec.InitContainers = c
return b
func (b *GenericJobBuilder) AddInitContainers(containers []corev1.Container) {
b.initContainers = append(b.initContainers, containers...)

}

func (b *GenericJobBuilder) AddInitContainer(container corev1.Container) JobBuilder {
return b.AddInitContainers([]corev1.Container{container})
func (b *GenericJobBuilder) AddInitContainer(container corev1.Container) {
b.AddInitContainers([]corev1.Container{container})
}

func (b *GenericJobBuilder) ResetInitContainers(containers []corev1.Container) JobBuilder {
b.GetObject().Spec.Template.Spec.InitContainers = containers
return b
func (b *GenericJobBuilder) ResetInitContainers(containers []corev1.Container) {
b.initContainers = containers
}

func (b *GenericJobBuilder) GetInitContainers() []corev1.Container {
return b.GetObject().Spec.Template.Spec.InitContainers
return b.initContainers
}

func (b *GenericJobBuilder) AddVolumes(volumes []corev1.Volume) JobBuilder {
v := b.GetObject().Spec.Template.Spec.Volumes
v = append(v, volumes...)
b.GetObject().Spec.Template.Spec.Volumes = v
return b
func (b *GenericJobBuilder) AddVolumes(volumes []corev1.Volume) {
b.volumes = append(b.volumes, volumes...)

}

func (b *GenericJobBuilder) AddVolume(volume corev1.Volume) JobBuilder {
return b.AddVolumes([]corev1.Volume{volume})
func (b *GenericJobBuilder) AddVolume(volume corev1.Volume) {
b.AddVolumes([]corev1.Volume{volume})
}

func (b *GenericJobBuilder) ResetVolumes(volumes []corev1.Volume) JobBuilder {
b.GetObject().Spec.Template.Spec.Volumes = volumes
return b
func (b *GenericJobBuilder) ResetVolumes(volumes []corev1.Volume) {
b.volumes = volumes
}

func (b *GenericJobBuilder) GetVolumes() []corev1.Volume {
return b.GetObject().Spec.Template.Spec.Volumes
return b.volumes
}

func (b *GenericJobBuilder) SetRestPolicy(policy corev1.RestartPolicy) {
b.resetPolicy = policy
}

func (b *GenericJobBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {
func (b *GenericJobBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
return b.GetObject(), nil
}
148 changes: 148 additions & 0 deletions pkg/builder/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package builder

import (
apiv1alpha1 "github.com/zncdatadev/superset-operator/pkg/apis/v1alpha1"
"github.com/zncdatadev/superset-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
)

var (
MatchingLabelsNames = []string{
"app.kubernetes.io/name",
"app.kubernetes.io/instance",
"app.kubernetes.io/role-group",
"app.kubernetes.io/component",
}
)

type Options interface {
GetName() string
GetFullName() string
GetLabels() map[string]string
AddLabels(labels map[string]string)
GetMatchingLabels() map[string]string
GetAnnotations() map[string]string

GetClusterOperation() *apiv1alpha1.ClusterOperationSpec
GetImage() *util.Image
GetPorts() []corev1.ContainerPort
SetPorts(ports []corev1.ContainerPort)
}

var _ Options = &ClusterOptions{}

type ClusterOptions struct {
Name string
Namespace string
Labels map[string]string
Annotations map[string]string
ClusterOperation *apiv1alpha1.ClusterOperationSpec
Image *util.Image
Ports []corev1.ContainerPort
}

func (o *ClusterOptions) GetName() string {
return o.Name
}

func (o *ClusterOptions) GetFullName() string {
return o.Name
}

func (o *ClusterOptions) GetLabels() map[string]string {
return o.Labels
}

func (o *ClusterOptions) GetAnnotations() map[string]string {
return o.Annotations
}

func (o *ClusterOptions) AddLabels(labels map[string]string) {
for k, v := range labels {
o.Labels[k] = v
}
}

func (o *ClusterOptions) filterLabels(labels map[string]string) map[string]string {

matchingLabels := make(map[string]string)
for _, label := range MatchingLabelsNames {
if value, ok := labels[label]; ok {
matchingLabels[label] = value
}
}
return matchingLabels
}

func (o *ClusterOptions) GetMatchingLabels() map[string]string {
return o.filterLabels(o.GetLabels())
}

func (o *ClusterOptions) GetClusterOperation() *apiv1alpha1.ClusterOperationSpec {
return o.ClusterOperation
}

func (o *ClusterOptions) GetImage() *util.Image {
return o.Image
}

func (o *ClusterOptions) GetPorts() []corev1.ContainerPort {
return o.Ports
}

func (o *ClusterOptions) SetPorts(ports []corev1.ContainerPort) {
o.Ports = ports
}

type RoleOptions struct {
ClusterOptions
Name string
}

func (o *RoleOptions) GetName() string {
return o.Name
}

func (o *RoleOptions) GetFullName() string {
return o.ClusterOptions.Name + "-" + o.Name
}

func (o *RoleOptions) GetLabels() map[string]string {
labels := o.ClusterOptions.Labels
labels["app.kubernetes.io/component"] = o.Name
return labels
}

func (o *RoleOptions) GetMatchingLabels() map[string]string {
return o.filterLabels(o.GetLabels())
}

type RoleGroupOptions struct {
RoleOptions
Name string
Replicas *int32

PodDisruptionBudget *apiv1alpha1.PodDisruptionBudgetSpec

CommandOverrides []string
EnvOverrides map[string]string
PodOverrides *corev1.PodTemplateSpec
}

func (o *RoleGroupOptions) GetName() string {
return o.Name
}

func (o *RoleGroupOptions) GetFullName() string {
return o.RoleOptions.GetFullName() + "-" + o.Name
}

func (o *RoleGroupOptions) GetLabels() map[string]string {
labels := o.RoleOptions.GetLabels()
labels["app.kubernetes.io/role-group"] = o.Name
return labels
}

func (o *RoleGroupOptions) GetMatchingLabels() map[string]string {
return o.filterLabels(o.GetLabels())
}
40 changes: 20 additions & 20 deletions pkg/builder/rbac.go
Original file line number Diff line number Diff line change
@@ -43,13 +43,13 @@ type GenericServiceAccountBuilder struct {
}

func NewGenericServiceAccountBuilder(
client resourceClient.ResourceClient,
name string,
client *resourceClient.Client,
options Options,
) *GenericServiceAccountBuilder {
return &GenericServiceAccountBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
}
}
@@ -76,13 +76,13 @@ type GenericRoleBuilder struct {
}

func NewGenericRoleBuilder(
client resourceClient.ResourceClient,
name string,
client *resourceClient.Client,
options Options,
) *GenericRoleBuilder {
return &GenericRoleBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
}
}
@@ -109,13 +109,13 @@ type GenericRoleBindingBuilder struct {
}

func NewGenericRoleBindingBuilder(
client resourceClient.ResourceClient,
name string,
client *resourceClient.Client,
options Options,
) *GenericRoleBindingBuilder {
return &GenericRoleBindingBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
}
}
@@ -142,13 +142,13 @@ type GenericClusterRoleBuilder struct {
}

func NewGenericClusterRoleBuilder(
client resourceClient.ResourceClient,
name string,
client *resourceClient.Client,
options Options,
) *GenericClusterRoleBuilder {
return &GenericClusterRoleBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
}
}
@@ -177,13 +177,13 @@ type GenericClusterRoleBindingBuilder struct {
}

func NewGenericClusterRoleBindingBuilder(
client resourceClient.ResourceClient,
name string,
client *resourceClient.Client,
options Options,
) *GenericClusterRoleBindingBuilder {
return &GenericClusterRoleBindingBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
}
}
123 changes: 46 additions & 77 deletions pkg/builder/service.go
Original file line number Diff line number Diff line change
@@ -3,15 +3,31 @@ package builder
import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/client"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

func ContainerPort2ServicePort(port corev1.ContainerPort) *corev1.ServicePort {
target := intstr.FromString(port.Name)

if port.Name == "" {
target = intstr.FromInt32(port.ContainerPort)
}

return &corev1.ServicePort{
Name: port.Name,
Port: port.ContainerPort,
Protocol: port.Protocol,
TargetPort: target,
}
}

type ServiceBuilder interface {
Builder
AddPort(name string, port int32, protocol corev1.Protocol, targetPort int32)
GetObject() *corev1.Service
AddPort(port *corev1.ContainerPort)
GetPorts() []corev1.ServicePort
GetServiceType() corev1.ServiceType
}
@@ -21,102 +37,55 @@ var _ ServiceBuilder = &BaseServiceBuilder{}
type BaseServiceBuilder struct {
BaseResourceBuilder

// if you want to get ports, please use GetPorts() method
ports []corev1.ServicePort
}

func (b *BaseServiceBuilder) AddPort(name string, port int32, protocol corev1.Protocol, targetPort int32) {
b.ports = append(b.ports, corev1.ServicePort{
Name: name,
Port: port,
Protocol: protocol,
TargetPort: intstr.FromInt(int(targetPort)),
})
}

func (b *BaseServiceBuilder) GetPorts() []corev1.ServicePort {
return b.ports
}

func (b *BaseServiceBuilder) GetServiceType() corev1.ServiceType {
return corev1.ServiceTypeClusterIP
}

func (b *BaseServiceBuilder) Build(_ context.Context) (client.Object, error) {

obj := &corev1.Service{
func (b *BaseServiceBuilder) GetObject() *corev1.Service {
return &corev1.Service{
ObjectMeta: b.GetObjectMeta(),
Spec: corev1.ServiceSpec{
Ports: b.GetPorts(),
Selector: b.Client.GetMatchingLabels(),
Selector: b.Options.GetMatchingLabels(),
Type: b.GetServiceType(),
},
}
}

return obj, nil
func (b *BaseServiceBuilder) AddPort(port *corev1.ContainerPort) {
p := ContainerPort2ServicePort(*port)

b.ports = append(b.ports, *p)
}

// NewGenericServiceBuilder creates a new service builder with generic ports
// FIXME: There may be performance issues or fatal exceptions in the following code.
func NewGenericServiceBuilder[T corev1.ContainerPort | corev1.ServicePort](
client resourceClient.ResourceClient,
name string,
ports []T,
) *BaseServiceBuilder {
func (b *BaseServiceBuilder) GetPorts() []corev1.ServicePort {
optionsPorts := b.Options.GetPorts()
ports := b.ports

var svcPorts []corev1.ServicePort

switch any(new(T)).(type) {
case corev1.ContainerPort:
for _, tPort := range ports {
port, ok := any(tPort).(corev1.ContainerPort)
if !ok {
panic("invalid type")
}

svcPorts = append(svcPorts, corev1.ServicePort{
Name: port.Name,
Port: port.ContainerPort,
Protocol: port.Protocol,
})
}
case corev1.ServicePort:
var ok bool
svcPorts, ok = any(ports).([]corev1.ServicePort)
if !ok {
panic("invalid type")
}
for _, port := range optionsPorts {
ports = append(ports, *ContainerPort2ServicePort(port))
}

return &BaseServiceBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
},
ports: svcPorts,
}
return ports
}

func (b *BaseServiceBuilder) GetServiceType() corev1.ServiceType {
return corev1.ServiceTypeClusterIP
}

func (b *BaseServiceBuilder) Build(_ context.Context) (ctrlclient.Object, error) {
obj := b.GetObject()
return obj, nil
}

func NewServiceBuilder(
client resourceClient.ResourceClient,
name string,
ports []corev1.ContainerPort,
client *client.Client,
options Options,
) *BaseServiceBuilder {
var svcPorts []corev1.ServicePort

for _, port := range ports {
svcPorts = append(svcPorts, corev1.ServicePort{
Name: port.Name,
Port: port.ContainerPort,
Protocol: port.Protocol,
})
}

return &BaseServiceBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
ports: svcPorts,
}
}
195 changes: 133 additions & 62 deletions pkg/builder/statefulset.go
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ package builder
import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/util"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -14,62 +14,80 @@ import (
type StatefulSetBuilder interface {
Builder
GetObject() *appv1.StatefulSet
SetReplicas(replicas *int32) StatefulSetBuilder
AddContainers([]corev1.Container) StatefulSetBuilder
AddInitContainers([]corev1.Container) StatefulSetBuilder
AddVolumes([]corev1.Volume) StatefulSetBuilder
AddVolumeClaimTemplates([]corev1.PersistentVolumeClaim) StatefulSetBuilder
AddTerminationGracePeriodSeconds(int64) StatefulSetBuilder
AddAffinity(corev1.Affinity) StatefulSetBuilder
SetReplicas(replicas *int32)
GetReplicas() *int32

AddContainers(containers []corev1.Container)
AddContainer(container corev1.Container)
ResetContainers(containers []corev1.Container)
GetContainers() []corev1.Container

AddInitContainers(containers []corev1.Container)
AddInitContainer(container corev1.Container)
ResetInitContainers(containers []corev1.Container)
GetInitContainers() []corev1.Container

AddVolumes(volumes []corev1.Volume)
AddVolume(volume corev1.Volume)
ResetVolumes(volumes []corev1.Volume)
GetVolumes() []corev1.Volume

AddVolumeClaimTemplates(claims []corev1.PersistentVolumeClaim)
AddVolumeClaimTemplate(claim corev1.PersistentVolumeClaim)
ResetVolumeClaimTemplates(claims []corev1.PersistentVolumeClaim)
GetVolumeClaimTemplates() []corev1.PersistentVolumeClaim

AddTerminationGracePeriodSeconds(i int64)
GetTerminationGracePeriodSeconds() *int64

AddAffinity(affinity corev1.Affinity)
GetAffinity() *corev1.Affinity
}

var _ StatefulSetBuilder = &GenericStatefulSetBuilder{}

type GenericStatefulSetBuilder struct {
BaseResourceBuilder
EnvOverrides map[string]string
CommandOverrides []string
Image util.Image

obj *appv1.StatefulSet
Options *RoleGroupOptions

replicas *int32
initContainers []corev1.Container
containers []corev1.Container
volumes []corev1.Volume
volumeClaimTemplates []corev1.PersistentVolumeClaim
terminationGracePeriodSeconds *int64
affinity *corev1.Affinity
}

func NewGenericStatefulSetBuilder(
client resourceClient.ResourceClient,
name string,
envOverrides map[string]string,
commandOverrides []string,
image util.Image,
client *client.Client,
options *RoleGroupOptions,
) *GenericStatefulSetBuilder {
return &GenericStatefulSetBuilder{
BaseResourceBuilder: BaseResourceBuilder{
Client: client,
Name: name,
Client: client,
Options: options,
},
EnvOverrides: envOverrides,
CommandOverrides: commandOverrides,
Image: image,
Options: options,
}
}

func (b *GenericStatefulSetBuilder) GetObject() *appv1.StatefulSet {
if b.obj == nil {
b.obj = &appv1.StatefulSet{
ObjectMeta: b.GetObjectMeta(),
Spec: appv1.StatefulSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: b.Client.GetMatchingLabels(),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Client.GetLabels(),
Annotations: b.Client.GetAnnotations(),
},
obj := &appv1.StatefulSet{
ObjectMeta: b.GetObjectMeta(),
Spec: appv1.StatefulSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: b.Options.GetMatchingLabels(),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: b.Options.GetLabels(),
Annotations: b.Options.GetAnnotations(),
},
},
}
},
}
return b.obj
return obj
}

func (b *GenericStatefulSetBuilder) Build(ctx context.Context) (ctrlclient.Object, error) {
@@ -78,10 +96,10 @@ func (b *GenericStatefulSetBuilder) Build(ctx context.Context) (ctrlclient.Objec
if len(obj.Spec.Template.Spec.Containers) == 0 {
obj.Spec.Template.Spec.Containers = []corev1.Container{
{
Name: b.Name,
Image: b.Image.String(),
Env: util.EnvsToEnvVars(b.EnvOverrides),
Command: b.CommandOverrides,
Name: b.Options.Name,
Image: b.Options.GetImage().String(),
Env: util.EnvsToEnvVars(b.Options.EnvOverrides),
Command: b.Options.CommandOverrides,
},
}
}
@@ -94,37 +112,90 @@ func (b *GenericStatefulSetBuilder) Build(ctx context.Context) (ctrlclient.Objec
return obj, nil
}

func (b *GenericStatefulSetBuilder) SetReplicas(replicas *int32) StatefulSetBuilder {
b.GetObject().Spec.Replicas = replicas
return b
func (b *GenericStatefulSetBuilder) SetReplicas(replicas *int32) {
b.replicas = replicas
}

func (b *GenericStatefulSetBuilder) GetReplicas() *int32 {
return b.replicas
}

func (b *GenericStatefulSetBuilder) AddContainer(container corev1.Container) {
b.containers = append(b.containers, container)
}

func (b *GenericStatefulSetBuilder) ResetContainers(containers []corev1.Container) {
b.containers = containers
}

func (b *GenericStatefulSetBuilder) AddContainers(containers []corev1.Container) {
b.containers = append(b.containers, containers...)
}

func (b *GenericStatefulSetBuilder) GetContainers() []corev1.Container {
return b.containers
}

func (b *GenericStatefulSetBuilder) AddInitContainer(container corev1.Container) {
b.initContainers = append(b.initContainers, container)
}

func (b *GenericStatefulSetBuilder) AddInitContainers(containers []corev1.Container) {
b.initContainers = append(b.initContainers, containers...)
}

func (b *GenericStatefulSetBuilder) ResetInitContainers(containers []corev1.Container) {
b.initContainers = containers
}

func (b *GenericStatefulSetBuilder) GetInitContainers() []corev1.Container {
return b.initContainers
}

func (b *GenericStatefulSetBuilder) AddVolume(volume corev1.Volume) {
b.volumes = append(b.volumes, volume)
}

func (b *GenericStatefulSetBuilder) AddVolumes(volumes []corev1.Volume) {
b.volumes = append(b.volumes, volumes...)
}

func (b *GenericStatefulSetBuilder) ResetVolumes(volumes []corev1.Volume) {
b.volumes = volumes
}

func (b *GenericStatefulSetBuilder) GetVolumes() []corev1.Volume {
return b.volumes
}

func (b *GenericStatefulSetBuilder) AddVolumeClaimTemplate(claim corev1.PersistentVolumeClaim) {
b.volumeClaimTemplates = append(b.volumeClaimTemplates, claim)
}

func (b *GenericStatefulSetBuilder) AddVolumeClaimTemplates(claims []corev1.PersistentVolumeClaim) {
b.volumeClaimTemplates = append(b.volumeClaimTemplates, claims...)
}

func (b *GenericStatefulSetBuilder) AddContainers(containers []corev1.Container) StatefulSetBuilder {
b.GetObject().Spec.Template.Spec.Containers = containers
return b
func (b *GenericStatefulSetBuilder) ResetVolumeClaimTemplates(claims []corev1.PersistentVolumeClaim) {
b.volumeClaimTemplates = claims
}

func (b *GenericStatefulSetBuilder) AddInitContainers(containers []corev1.Container) StatefulSetBuilder {
b.GetObject().Spec.Template.Spec.InitContainers = containers
return b
func (b *GenericStatefulSetBuilder) GetVolumeClaimTemplates() []corev1.PersistentVolumeClaim {
return b.volumeClaimTemplates
}

func (b *GenericStatefulSetBuilder) AddVolumes(volumes []corev1.Volume) StatefulSetBuilder {
b.GetObject().Spec.Template.Spec.Volumes = volumes
return b
func (b *GenericStatefulSetBuilder) GetTerminationGracePeriodSeconds() *int64 {
return b.terminationGracePeriodSeconds
}

func (b *GenericStatefulSetBuilder) AddVolumeClaimTemplates(claims []corev1.PersistentVolumeClaim) StatefulSetBuilder {
b.GetObject().Spec.VolumeClaimTemplates = claims
return b
func (b *GenericStatefulSetBuilder) AddTerminationGracePeriodSeconds(i int64) {
b.terminationGracePeriodSeconds = &i
}

func (b *GenericStatefulSetBuilder) AddTerminationGracePeriodSeconds(i int64) StatefulSetBuilder {
b.GetObject().Spec.Template.Spec.TerminationGracePeriodSeconds = &i
return b
func (b *GenericStatefulSetBuilder) AddAffinity(affinity corev1.Affinity) {
b.affinity = &affinity
}

func (b *GenericStatefulSetBuilder) AddAffinity(affinity corev1.Affinity) StatefulSetBuilder {
b.GetObject().Spec.Template.Spec.Affinity = &affinity
return b
func (b *GenericStatefulSetBuilder) GetAffinity() *corev1.Affinity {
return b.affinity
}
71 changes: 18 additions & 53 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
@@ -10,78 +10,43 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var (
clientLogger = ctrl.Log.WithName("resourceClient")
MatchingLabelsNames = []string{
"app.kubernetes.io/name",
"app.kubernetes.io/instance",
"app.kubernetes.io/role-group",
"app.kubernetes.io/component",
}
clientLogger = ctrl.Log.WithName("resourceClient")
)

type ResourceClient struct {
Client client.Client

OwnerReference client.Object

Labels map[string]string
Annotations map[string]string
}

func (c *ResourceClient) AddLabels(labels map[string]string, override bool) {
for k, v := range labels {
if _, ok := c.Labels[k]; !ok || override {
c.Labels[k] = v
}
}
}
type Client struct {
Client ctrlclient.Client

func (c *ResourceClient) GetMatchingLabels() map[string]string {

matchingLabels := make(map[string]string)
for _, label := range MatchingLabelsNames {
if value, ok := c.Labels[label]; ok {
matchingLabels[label] = value
}
}
return matchingLabels
}

func (c *ResourceClient) AddAnnotations(annotations map[string]string, override bool) {
for k, v := range annotations {
if _, ok := c.Annotations[k]; !ok || override {
c.Annotations[k] = v
}
}
OwnerReference ctrlclient.Object
}

func (c *ResourceClient) GetLabels() map[string]string {
return c.Labels
func (c *Client) GetCtrlClient() ctrlclient.Client {
return c.Client
}

func (c *ResourceClient) GetAnnotations() map[string]string {
return c.Annotations
func (c *Client) GetCtrlScheme() *runtime.Scheme {
return c.Client.Scheme()
}

func (c *ResourceClient) GetOwnerReference() client.Object {
func (c *Client) GetOwnerReference() ctrlclient.Object {
return c.OwnerReference
}
func (c *ResourceClient) GetOwnerNamespace() string {
func (c *Client) GetOwnerNamespace() string {
return c.OwnerReference.GetNamespace()
}

func (c *ResourceClient) GetOwnerName() string {
func (c *Client) GetOwnerName() string {
return c.OwnerReference.GetName()
}

// Get the object from the cluster
// If the object has no namespace, it will use the owner namespace
func (c *ResourceClient) Get(ctx context.Context, obj client.Object) error {
func (c *Client) Get(ctx context.Context, obj ctrlclient.Object) error {
name := obj.GetName()
namespace := obj.GetNamespace()
if namespace == "" {
@@ -93,7 +58,7 @@ func (c *ResourceClient) Get(ctx context.Context, obj client.Object) error {
)
}
kind := obj.GetObjectKind()
if err := c.Client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, obj); err != nil {
if err := c.Client.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: name}, obj); err != nil {
opt := []any{"ns", namespace, "name", name, "kind", kind}
if apierrors.IsNotFound(err) {
clientLogger.V(0).Info("Fetch resource NotFound", opt...)
@@ -105,16 +70,16 @@ func (c *ResourceClient) Get(ctx context.Context, obj client.Object) error {
return nil
}

func (c *ResourceClient) CreateOrUpdate(ctx context.Context, obj client.Object) (mutation bool, err error) {
func (c *Client) CreateOrUpdate(ctx context.Context, obj ctrlclient.Object) (mutation bool, err error) {

key := client.ObjectKeyFromObject(obj)
key := ctrlclient.ObjectKeyFromObject(obj)
namespace := obj.GetNamespace()
kinds, _, _ := c.Client.Scheme().ObjectKinds(obj)
name := obj.GetName()

clientLogger.V(5).Info("Creating or updating object", "Kind", kinds, "Namespace", namespace, "Name", name)

current := obj.DeepCopyObject().(client.Object)
current := obj.DeepCopyObject().(ctrlclient.Object)
// Check if the object exists, if not create a new one
err = c.Client.Get(ctx, key, current)
var calculateOpt = []patch.CalculateOption{
2 changes: 1 addition & 1 deletion pkg/client/database.go
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ type DatabaseConfiguration struct {
DbInline *DatabaseParams
Namespace string
Context context.Context
Client ResourceClient
Client *Client
}

func (d *DatabaseConfiguration) GetNamespace() string {
4 changes: 2 additions & 2 deletions pkg/client/example_test.go
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func ExampleResourceClient_Get() {
client := &ResourceClient{}
func ExampleClient_Get() {
client := &Client{}

// Get a service in the same namespace as the owner object
svcInOwnerNamespace := &corev1.Service{
73 changes: 13 additions & 60 deletions pkg/reconciler/cluster.go
Original file line number Diff line number Diff line change
@@ -5,9 +5,8 @@ import (
"reflect"

apiv1alpha1 "github.com/zncdatadev/superset-operator/pkg/apis/v1alpha1"
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
)

@@ -18,30 +17,27 @@ var (
type ClusterReconciler interface {
Reconciler
GetClusterOperation() *apiv1alpha1.ClusterOperationSpec
GetImage() util.Image
GetResources() []Reconciler
AddResource(resource Reconciler)
RegisterResources(ctx context.Context) error
}

type BaseClusterReconciler[T AnySpec] struct {
BaseReconciler[T]
ClusterInfo *ClusterInfo
resources []Reconciler
resources []Reconciler
}

func NewBaseClusterReconciler[T AnySpec](
client client.ResourceClient,
clusterInfo *ClusterInfo,
client *client.Client,
options builder.Options,
spec T,
) *BaseClusterReconciler[T] {
return &BaseClusterReconciler[T]{
BaseReconciler: BaseReconciler[T]{
Client: client,
Name: clusterInfo.Name,
Spec: spec,
Client: client,
Options: options,
Spec: spec,
},
ClusterInfo: clusterInfo,
}
}

@@ -84,8 +80,7 @@ var _ RoleReconciler = &BaseRoleReconciler[AnySpec]{}

type BaseRoleReconciler[T AnySpec] struct {
BaseClusterReconciler[T]

RoleInfo *RoleInfo
Options *builder.RoleOptions
}

// MergeRoleGroupSpec
@@ -124,11 +119,7 @@ func (b *BaseRoleReconciler[T]) MergeRoleGroupSpec(roleGroup any) {
}

func (b *BaseRoleReconciler[T]) GetClusterOperation() *apiv1alpha1.ClusterOperationSpec {
return b.RoleInfo.ClusterInfo.ClusterOperation
}

func (b *BaseRoleReconciler[T]) GetImage() util.Image {
return b.RoleInfo.ClusterInfo.Image
return b.Options.GetClusterOperation()
}

func (b *BaseRoleReconciler[T]) Ready(ctx context.Context) Result {
@@ -141,55 +132,17 @@ func (b *BaseRoleReconciler[T]) Ready(ctx context.Context) Result {
}

func NewBaseRoleReconciler[T AnySpec](
client client.ResourceClient,
roleInfo *RoleInfo,
client *client.Client,
roleOptions *builder.RoleOptions,
spec T,
) *BaseRoleReconciler[T] {

client.AddLabels(
map[string]string{
"app.kubernetes.io/component": roleInfo.Name,
},
false,
)

return &BaseRoleReconciler[T]{
BaseClusterReconciler: *NewBaseClusterReconciler[T](
client,
&roleInfo.ClusterInfo,
roleOptions,
spec,
),
RoleInfo: roleInfo,
Options: roleOptions,
}
}

type ClusterInfo struct {
Name string
Namespace string
ClusterOperation *apiv1alpha1.ClusterOperationSpec
Image util.Image
}

type RoleInfo struct {
ClusterInfo
Name string
}

func (r *RoleInfo) GetFullName() string {
return r.ClusterInfo.Name + "-" + r.Name
}

type RoleGroupInfo struct {
RoleInfo
Name string
Replicas *int32

PodDisruptionBudget *apiv1alpha1.PodDisruptionBudgetSpec
CommandOverrides []string
EnvOverrides map[string]string
PodOverrides *corev1.PodTemplateSpec
}

func (r *RoleGroupInfo) GetFullName() string {
return r.RoleInfo.GetFullName() + "-" + r.Name
}
36 changes: 16 additions & 20 deletions pkg/reconciler/deployment.go
Original file line number Diff line number Diff line change
@@ -6,34 +6,34 @@ import (
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
)

var _ ResourceReconciler[builder.DeploymentBuilder] = &DeploymentReconciler{}

// TODO should remove AnySpec? Now builder requires AnySpec, and reconciler requires builder,

type DeploymentReconciler struct {
GenericResourceReconciler[AnySpec, builder.DeploymentBuilder]
Ports []corev1.ContainerPort
RoleGroupInfo *RoleGroupInfo
GenericResourceReconciler[builder.DeploymentBuilder]
Options *builder.RoleGroupOptions
}

// getReplicas returns the number of replicas for the role group.
// handle cluster operation stopped state.
func (r *DeploymentReconciler) getReplicas() *int32 {
if r.RoleGroupInfo.ClusterOperation != nil && r.RoleGroupInfo.ClusterOperation.Stopped {
clusterOperations := r.Options.GetClusterOperation()
if clusterOperations != nil && clusterOperations.Stopped {
logger.Info("Cluster operation stopped, set replicas to 0")
zero := int32(0)
return &zero
}
return r.RoleGroupInfo.Replicas
return nil
}

func (r *DeploymentReconciler) Reconcile(ctx context.Context) Result {
resource, err := r.GetBuilder().
SetReplicas(r.getReplicas()).
Build(ctx)
resourceBuilder := r.GetBuilder()
replicas := r.getReplicas()
if replicas != nil {
resourceBuilder.SetReplicas(replicas)
}
resource, err := resourceBuilder.Build(ctx)

if err != nil {
return NewResult(true, 0, err)
@@ -43,7 +43,7 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context) Result {

func (r *DeploymentReconciler) Ready(ctx context.Context) Result {
obj := appv1.Deployment{}
if err := r.GetClient().Get(ctx, &obj); err != nil {
if err := r.Client.Get(ctx, &obj); err != nil {
return NewResult(true, 0, err)
}
if obj.Status.ReadyReplicas == *obj.Spec.Replicas {
@@ -55,19 +55,15 @@ func (r *DeploymentReconciler) Ready(ctx context.Context) Result {
}

func NewDeploymentReconciler(
client client.ResourceClient,
roleGroupInfo *RoleGroupInfo,
ports []corev1.ContainerPort,
client *client.Client,
options *builder.RoleGroupOptions,
deployBuilder builder.DeploymentBuilder,
) *DeploymentReconciler {
return &DeploymentReconciler{
GenericResourceReconciler: *NewGenericResourceReconciler[AnySpec, builder.DeploymentBuilder](
GenericResourceReconciler: *NewGenericResourceReconciler[builder.DeploymentBuilder](
client,
roleGroupInfo.GetFullName(),
nil,
options,
deployBuilder,
),
RoleGroupInfo: roleGroupInfo,
Ports: ports,
}
}
29 changes: 12 additions & 17 deletions pkg/reconciler/reconciler.go
Original file line number Diff line number Diff line change
@@ -3,50 +3,45 @@ package reconciler
import (
"context"

resourceClient "github.com/zncdatadev/superset-operator/pkg/client"
"github.com/zncdatadev/superset-operator/pkg/builder"
"github.com/zncdatadev/superset-operator/pkg/client"
"k8s.io/apimachinery/pkg/runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

type AnySpec any

type Reconciler interface {
GetClient() *resourceClient.ResourceClient
GetClient() *client.Client
GetCtrlClient() ctrlclient.Client
GetCtrlScheme() *runtime.Scheme
Reconcile(ctx context.Context) Result
Ready(ctx context.Context) Result
GetNameWithSuffix(suffix string) string
}

var _ Reconciler = &BaseReconciler[AnySpec]{}

type BaseReconciler[T AnySpec] struct {
// Do not use ptr, to avoid other packages to modify the client
Client resourceClient.ResourceClient
Name string

Spec T
Client *client.Client
Options builder.Options
Spec T
}

func (b *BaseReconciler[T]) GetClient() *resourceClient.ResourceClient {
return &b.Client
}

func (b *BaseReconciler[T]) GetCtrlClient() ctrlclient.Client {
return b.Client.Client
func (b *BaseReconciler[T]) GetClient() *client.Client {
return b.Client
}

func (b *BaseReconciler[T]) GetName() string {
return b.Name
return b.Options.GetFullName()
}

func (b *BaseReconciler[T]) GetNameWithSuffix(suffix string) string {
return b.Name + "-" + suffix
func (b *BaseReconciler[T]) GetCtrlClient() ctrlclient.Client {
return b.Client.GetCtrlClient()
}

func (b *BaseReconciler[T]) GetCtrlScheme() *runtime.Scheme {
return b.Client.Client.Scheme()
return b.Client.GetCtrlScheme()
}

func (b *BaseReconciler[T]) Ready(ctx context.Context) Result {
Loading

0 comments on commit 74e4b13

Please sign in to comment.