Skip to content

Commit

Permalink
refactor: refactor state/config managers and backend
Browse files Browse the repository at this point in the history
Signed-off-by: Bird <aflybird0@gmail.com>
  • Loading branch information
aFlyBird0 committed Aug 19, 2022
1 parent ff7df5f commit 0e9f896
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 83 deletions.
22 changes: 11 additions & 11 deletions internal/pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,24 @@ type Backend interface {
}

// GetBackend will return a Backend by the given name.
func GetBackend(stateConfig configmanager.State) (Backend, error) {
typeName := Type(stateConfig.Backend)
func GetBackend(state configmanager.State) (Backend, error) {
typeName := Type(state.Backend)
switch typeName {
case Local:
log.Debugf("Used the Backend: %s.", typeName)
return local.NewLocal(stateConfig.Options.StateFile), nil
log.Infof("Using local backend. State file: %s.", state.Options.StateFile)
return local.NewLocal(state.Options.StateFile), nil
case S3:
log.Debugf("Used the Backend: %s.", typeName)
log.Infof("Using s3 backend. Bucket: %s, region: %s, key: %s.", state.Options.Bucket, state.Options.Region, state.Options.Key)
return s3.NewS3Backend(
stateConfig.Options.Bucket,
stateConfig.Options.Region,
stateConfig.Options.Key,
state.Options.Bucket,
state.Options.Region,
state.Options.Key,
), nil
case K8s:
log.Debugf("Used the Backend: %s.", typeName)
log.Infof("Using configmap backend. Namespace: %s, ConfigMap name: %s.", state.Options.Namespace, state.Options.ConfigMap)
return k8s.NewBackend(
stateConfig.Options.Namespace,
stateConfig.Options.ConfigMap)
state.Options.Namespace,
state.Options.ConfigMap)
default:
return nil, fmt.Errorf("the backend type < %s > is illegal", typeName)
}
Expand Down
10 changes: 1 addition & 9 deletions internal/pkg/backend/k8s/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import (
)

const (
defaultNamespace = "devstream"
defaultConfigMapName = "devstream-state"
stateKey = "state"
stateKey = "state"
)

type Backend struct {
Expand All @@ -33,12 +31,6 @@ func NewBackend(namespace, configMapName string) (*Backend, error) {
configMapName: configMapName,
client: c,
}
if b.namespace == "" {
b.namespace = defaultNamespace
}
if b.configMapName == "" {
b.configMapName = defaultConfigMapName
}

return b, nil
}
Expand Down
24 changes: 11 additions & 13 deletions internal/pkg/backend/k8s/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"context"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/client-go/applyconfigurations/core/v1"
"k8s.io/client-go/tools/record/util"

"github.com/devstream-io/devstream/pkg/util/log"
)
Expand Down Expand Up @@ -53,36 +53,34 @@ func (b *Backend) getOrCreateConfigMap() (*v1.ConfigMap, error) {
}

// if configmap not exist, create it
log.Info("configmap not exist, will create it")
log.Infof("configmap %s in namespace %s not exist, will create it", b.configMapName, b.namespace)
return b.applyConfigMap("")
}

func (b *Backend) getConfigMap() (cm *v1.ConfigMap, exist bool, err error) {
configMap, err := b.client.CoreV1().ConfigMaps(b.namespace).Get(context.Background(), b.configMapName, metav1.GetOptions{})
// if configmap not exist, return nil
if util.IsKeyNotFoundError(err) {
if errors.IsNotFound(err) {
return nil, false, nil
}
if err != nil {
return nil, false, err
}

log.Debugf("configmap %s found, detail: %s", configMap.Name, configMap.String())
log.Debugf("configmap %s in namespace %s found, detail: %v", configMap.Name, configMap.Namespace, configMap)

return configMap, true, nil
}

func (b *Backend) createNamespaceIfNotExist() error {
_, err := b.client.CoreV1().Namespaces().Get(context.Background(), b.namespace, metav1.GetOptions{})
// if namespace not exist, try to create it
if util.IsKeyNotFoundError(err) {
exist, err := b.client.IsNamespaceExists(b.namespace)
if err != nil {
return err
}
if !exist {
log.Infof("namespace %s not exist, will create it", b.namespace)
_, err = b.client.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: b.namespace,
},
}, metav1.CreateOptions{})
return b.client.CreateNamespace(b.namespace)
}

return err
return nil
}
2 changes: 1 addition & 1 deletion internal/pkg/configmanager/configmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (m *Manager) renderConfigs(coreConfigBytes, variablesConfigBytes, toolsConf
log.Errorf("Please verify the format of your core config. Error: %s.", err)
return nil, err
}
if ok, err := coreConfig.Validate(); !ok {
if err := coreConfig.ValidateAndDefault(); err != nil {
return nil, err
}
state := coreConfig.State
Expand Down
73 changes: 45 additions & 28 deletions internal/pkg/configmanager/coreconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
"github.com/devstream-io/devstream/pkg/util/log"
)

const (
defaultNamespace = "devstream"
defaultConfigMapName = "devstream-state"
)

// CoreConfig is the struct representing the complete original configuration YAML files.
type CoreConfig struct {
// TODO(daniel-hutao): Relative path support
Expand Down Expand Up @@ -39,46 +44,58 @@ type StateConfigOptions struct {
ConfigMap string `yaml:"configmap"`
}

func (c *CoreConfig) Validate() (bool, error) {
func (c *CoreConfig) ValidateAndDefault() error {
if c.State == nil {
return false, fmt.Errorf("state config is empty")
return fmt.Errorf("state config is empty")
}

errors := make([]error, 0)

log.Infof("Got Backend from config: %s", c.State.Backend)
switch c.State.Backend {
case "local":
if c.State.Options.StateFile == "" {
log.Debugf("The stateFile has not been set, default value %s will be used.", local.DefaultStateFile)
c.State.Options.StateFile = local.DefaultStateFile
}
case "s3":
if c.State.Options.Bucket == "" {
errors = append(errors, fmt.Errorf("state s3 Bucket is empty"))
}
if c.State.Options.Region == "" {
errors = append(errors, fmt.Errorf("state s3 Region is empty"))
}
if c.State.Options.Key == "" {
errors = append(errors, fmt.Errorf("state s3 Key is empty"))
}
case "k8s":
default:
errors = append(errors, fmt.Errorf("backend type error"))
}

if len(errors) != 0 {
errs := ValidateAndDefaultBackend(c.State)
if len(errs) != 0 {
var retErr []string
log.Error("Config file validation failed.")
for i, err := range errors {
for i, err := range errs {
log.Errorf("%d -> %s.", i+1, err)
retErr = append(retErr, err.Error())
}
return false, fmt.Errorf("%s", strings.Join(retErr, "; "))
return fmt.Errorf("%s", strings.Join(retErr, "; "))
}

return nil
}

func ValidateAndDefaultBackend(state *State) []error {
var errs []error
switch {
case state.Backend == "local":
if state.Options.StateFile == "" {
log.Debugf("The stateFile has not been set, default value %s will be used.", local.DefaultStateFile)
state.Options.StateFile = local.DefaultStateFile
}
case state.Backend == "s3":
if state.Options.Bucket == "" {
errs = append(errs, fmt.Errorf("state s3 Bucket is empty"))
}
if state.Options.Region == "" {
errs = append(errs, fmt.Errorf("state s3 Region is empty"))
}
if state.Options.Key == "" {
errs = append(errs, fmt.Errorf("state s3 Key is empty"))
}
case strings.ToLower(state.Backend) == "k8s" || strings.ToLower(state.Backend) == "kubernetes":
state.Backend = "k8s"
if state.Options.Namespace == "" {
state.Options.Namespace = defaultNamespace
}
if state.Options.ConfigMap == "" {
state.Options.ConfigMap = defaultConfigMapName
}
default:
errs = append(errs, fmt.Errorf("the backend type < %s > is illegal", state.Backend))
}

return true, nil
return errs
}

func (c *CoreConfig) ParseVarFilePath() error {
Expand Down
15 changes: 5 additions & 10 deletions internal/pkg/configmanager/coreconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ var _ = Describe("Core Config", func() {
Context("Validate method", func() {
It("should return error if state config is empty", func() {
coreConfig := configmanager.CoreConfig{State: nil}
isValid, err := coreConfig.Validate()
Expect(isValid).Should(BeFalse())
err := coreConfig.ValidateAndDefault()
Expect(err).Error().Should(HaveOccurred())
Expect(err.Error()).Should(Equal("state config is empty"))
})
Expand All @@ -23,8 +22,7 @@ var _ = Describe("Core Config", func() {
Backend: "not_exist",
},
}
isValid, err := coreConfig.Validate()
Expect(isValid).Should(BeFalse())
err := coreConfig.ValidateAndDefault()
Expect(err).Error().Should(HaveOccurred())
Expect(err.Error()).Should(
ContainSubstring("backend type error"),
Expand All @@ -35,8 +33,7 @@ var _ = Describe("Core Config", func() {
coreConfig := configmanager.CoreConfig{
State: &configmanager.State{Backend: "s3"},
}
isValid, err := coreConfig.Validate()
Expect(isValid).Should(BeFalse())
err := coreConfig.ValidateAndDefault()
Expect(err).Error().Should(HaveOccurred())
Expect(err.Error()).Should(
ContainSubstring("state s3 Bucket is empty"),
Expand All @@ -47,8 +44,7 @@ var _ = Describe("Core Config", func() {
coreConfig := configmanager.CoreConfig{
State: &configmanager.State{Backend: "local"},
}
isValid, err := coreConfig.Validate()
Expect(isValid).Should(BeTrue())
err := coreConfig.ValidateAndDefault()
Expect(err).Error().ShouldNot(HaveOccurred())
})

Expand All @@ -64,8 +60,7 @@ var _ = Describe("Core Config", func() {
},
},
}
isValid, err := coreConfig.Validate()
Expect(isValid).Should(BeTrue())
err := coreConfig.ValidateAndDefault()
Expect(err).Error().ShouldNot(HaveOccurred())
})
})
Expand Down
12 changes: 1 addition & 11 deletions internal/pkg/statemanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package statemanager

import (
"fmt"
"strings"

"gopkg.in/yaml.v3"

Expand Down Expand Up @@ -50,16 +49,7 @@ func NewManager(stateConfig configmanager.State) (Manager, error) {
log.Debugf("The global manager m is not initialized.")

// Get the backend from config
if stateConfig.Backend == "local" {
log.Infof("Using local backend. State file: %s.", stateConfig.Options.StateFile)
} else if stateConfig.Backend == "s3" {
log.Infof("Using s3 backend. Bucket: %s, region: %s, key: %s.", stateConfig.Options.Bucket, stateConfig.Options.Region, stateConfig.Options.Key)
} else if strings.ToLower(stateConfig.Backend) == "k8s" || strings.ToLower(stateConfig.Backend) == "kubernetes" {
log.Infof("Using configmap backend. Namespace: %s, ConfigMap name: %s.", stateConfig.Options.Namespace, stateConfig.Options.ConfigMap)
} else {
return nil, fmt.Errorf("the backend type < %s > is illegal", stateConfig.Backend)
}

// backend config has been validated in configmanager.Manager.LoadConfig()
b, err := backend.GetBackend(stateConfig)
if err != nil {
log.Errorf("Failed to get the Backend: %s.", err)
Expand Down

0 comments on commit 0e9f896

Please sign in to comment.