Skip to content

Commit

Permalink
internal/controller: support custom atlas.hcl
Browse files Browse the repository at this point in the history
  • Loading branch information
datdao committed Dec 12, 2024
1 parent 7110c6d commit 2ad336e
Show file tree
Hide file tree
Showing 15 changed files with 1,287 additions and 33 deletions.
3 changes: 2 additions & 1 deletion api/v1alpha1/atlasmigration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ type (
}
// AtlasMigrationSpec defines the desired state of AtlasMigration
AtlasMigrationSpec struct {
TargetSpec `json:",inline"`
TargetSpec `json:",inline"`
ProjectConfigSpec `json:",inline"`
// EnvName sets the environment name used for reporting runs to Atlas Cloud.
EnvName string `json:"envName,omitempty"`
// Cloud defines the Atlas Cloud configuration.
Expand Down
3 changes: 2 additions & 1 deletion api/v1alpha1/atlasschema_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ type (
}
// AtlasSchemaSpec defines the desired state of AtlasSchema
AtlasSchemaSpec struct {
TargetSpec `json:",inline"`
TargetSpec `json:",inline"`
ProjectConfigSpec `json:",inline"`
// Desired Schema of the target.
Schema Schema `json:"schema,omitempty"`
// Cloud defines the Atlas Cloud configuration.
Expand Down
101 changes: 101 additions & 0 deletions api/v1alpha1/project_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2023 The Atlas Operator Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1alpha1

import (
"context"
"fmt"

"ariga.io/atlas-go-sdk/atlasexec"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type (
// ProjectConfigSpec defines the project configuration.
ProjectConfigSpec struct {
// Config defines the project configuration.
// Should be a valid YAML string.
Config string `json:"config,omitempty"`
// ConfigFrom defines the reference to the secret key that contains the project configuration.
ConfigFrom Secret `json:"configFrom,omitempty"`
// EnvName defines the environment name that defined in the project configuration.
// If not defined, the default environment "k8s" will be used.
EnvName string `json:"envName,omitempty"`
// Vars defines the input variables for the project configuration.
Vars []Variable `json:"vars,omitempty"`
}
// Variables defines the reference of secret/configmap to the input variables for the project configuration.
Variable struct {
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
ValueFrom ValueFrom `json:"valueFrom,omitempty"`
}
// ValueFrom defines the reference to the secret key that contains the value.
ValueFrom struct {
// SecretKeyRef defines the secret key reference to use for the value.
SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty"`
// ConfigMapKeyRef defines the configmap key reference to use for the value.
ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
}
)

// GetConfig returns the project configuration.
// The configuration is resolved from the secret reference.
func (s ProjectConfigSpec) GetConfig(ctx context.Context, r client.Reader, ns string) (string, error) {
if s.Config != "" {
return s.Config, nil
}
if s.ConfigFrom.SecretKeyRef != nil {
return getSecretValue(ctx, r, ns, s.ConfigFrom.SecretKeyRef)
}
return "", nil
}

// GetVars returns the input variables for the project configuration.
// The variables are resolved from the secret or configmap reference.
func (s ProjectConfigSpec) GetVars(ctx context.Context, r client.Reader, namespace string) (atlasexec.Vars2, error) {
vars := make(map[string]any)
for _, variable := range s.Vars {
var (
value string
err error
)
value = variable.Value
if variable.ValueFrom.SecretKeyRef != nil {
if value, err = getSecretValue(ctx, r, namespace, variable.ValueFrom.SecretKeyRef); err != nil {
return nil, err
}
}
if variable.ValueFrom.ConfigMapKeyRef != nil {
if value, err = getConfigMapValue(ctx, r, namespace, variable.ValueFrom.ConfigMapKeyRef); err != nil {
return nil, err
}
}
// Resolve variables with the same key by grouping them into a slice.
// It's necessary when generating Atlas command for list(string) input type.
if existingValue, exists := vars[variable.Key]; exists {
if _, ok := existingValue.([]string); ok {
vars[variable.Key] = append(existingValue.([]string), value)
} else if _, ok := existingValue.(string); ok {
vars[variable.Key] = []string{existingValue.(string), value}
} else {
return nil, fmt.Errorf("invalid variable type for %q", variable.Key)
}
}
vars[variable.Key] = value
}
return vars, nil
}
24 changes: 19 additions & 5 deletions api/v1alpha1/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type (
// DatabaseURL returns the database url.
func (s TargetSpec) DatabaseURL(ctx context.Context, r client.Reader, ns string) (*url.URL, error) {
if s.URLFrom.SecretKeyRef != nil {
val, err := getSecrectValue(ctx, r, ns, s.URLFrom.SecretKeyRef)
val, err := getSecretValue(ctx, r, ns, s.URLFrom.SecretKeyRef)
if err != nil {
return nil, err
}
Expand All @@ -76,21 +76,21 @@ func (s TargetSpec) DatabaseURL(ctx context.Context, r client.Reader, ns string)
return url.Parse(s.URL)
}
if s.Credentials.UserFrom.SecretKeyRef != nil {
val, err := getSecrectValue(ctx, r, ns, s.Credentials.UserFrom.SecretKeyRef)
val, err := getSecretValue(ctx, r, ns, s.Credentials.UserFrom.SecretKeyRef)
if err != nil {
return nil, err
}
s.Credentials.User = val
}
if s.Credentials.PasswordFrom.SecretKeyRef != nil {
val, err := getSecrectValue(ctx, r, ns, s.Credentials.PasswordFrom.SecretKeyRef)
val, err := getSecretValue(ctx, r, ns, s.Credentials.PasswordFrom.SecretKeyRef)
if err != nil {
return nil, err
}
s.Credentials.Password = val
}
if s.Credentials.HostFrom.SecretKeyRef != nil {
val, err := getSecrectValue(ctx, r, ns, s.Credentials.HostFrom.SecretKeyRef)
val, err := getSecretValue(ctx, r, ns, s.Credentials.HostFrom.SecretKeyRef)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -133,7 +133,7 @@ func (c *Credentials) URL() *url.URL {
return u
}

func getSecrectValue(
func getSecretValue(
ctx context.Context,
r client.Reader,
ns string,
Expand All @@ -147,6 +147,20 @@ func getSecrectValue(
return string(val.Data[ref.Key]), nil
}

func getConfigMapValue(
ctx context.Context,
r client.Reader,
ns string,
ref *corev1.ConfigMapKeySelector,
) (string, error) {
val := &corev1.ConfigMap{}
err := r.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: ns}, val)
if err != nil {
return "", err
}
return string(val.Data[ref.Key]), nil
}

// Driver defines the database driver.
type Driver string

Expand Down
66 changes: 66 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.

Loading

0 comments on commit 2ad336e

Please sign in to comment.