Refactor(cli): Refactor vela env, deprecate vela config (#2037)

* Refactor(cli): cut env to namespace, use application to save

Signed-off-by: qiaozp <chivalry.pp@gmail.com>

* Fix: test

* Fix: typo
This commit is contained in:
qiaozp
2021-10-08 13:10:47 +08:00
committed by GitHub
parent 0396d8d8bf
commit a574fc0fbf
46 changed files with 522 additions and 1776 deletions

View File

@@ -47,12 +47,13 @@ const (
StatusStaging = "Staging"
)
// EnvMeta stores the info for app environment
// Config contains key/value pairs
type Config map[string]string
// EnvMeta stores the namespace for app environment
type EnvMeta struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
Email string `json:"email,omitempty"`
Domain string `json:"domain,omitempty"`
Current string `json:"current,omitempty"`
}

View File

@@ -111,7 +111,7 @@ var ApplicationExecContext = func(context string, appName string) bool {
var ApplicationPortForwardContext = func(context string, appName string) bool {
return ginkgo.Context(context, func() {
ginkgo.It("should get output of portward successfully", func() {
ginkgo.It("should get output of port-forward successfully", func() {
cli := fmt.Sprintf("vela port-forward %s 80:80 ", appName)
output, err := e2e.ExecAndTerminate(cli)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
@@ -128,14 +128,6 @@ var ApplicationInitIntercativeCliContext = func(context string, appName string,
data := []struct {
q, a string
}{
{
q: "What is the domain of your application service (optional): ",
a: "testdomain",
},
{
q: "What is your email (optional, used to generate certification): ",
a: "test@mail",
},
{
q: "What would you like to name your application (required): ",
a: appName,

View File

@@ -33,7 +33,7 @@ var (
cli := fmt.Sprintf("vela env init %s", envName)
output, err := Exec(cli)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
expectedOutput := fmt.Sprintf("environment %s created,", envName)
expectedOutput := fmt.Sprintf("environment %s created", envName)
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
})
})
@@ -45,7 +45,7 @@ var (
cli := fmt.Sprintf("vela env init %s --namespace %s", envName, namespace)
output, err := Exec(cli)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
expectedOutput := fmt.Sprintf("environment %s created,", envName)
expectedOutput := fmt.Sprintf("environment %s created", envName)
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
})
})

2
e2e/env/env_test.go vendored
View File

@@ -32,7 +32,7 @@ var _ = ginkgo.Describe("Env", func() {
e2e.EnvInitContext("env init", envName)
e2e.EnvInitContext("env init another one", envName2)
e2e.EnvShowContext("env show", envName)
e2e.EnvSetContext("env sw", envName)
e2e.EnvSetContext("env set", envName)
ginkgo.Context("env list", func() {
ginkgo.It("should list all envs", func() {

View File

@@ -1,96 +0,0 @@
/*
Copyright 2021 The KubeVela 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 config
import (
"context"
"strings"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
// Splitter is a splitter for configmap name generation
Splitter = "-"
// TypeConfigMap defines the type of Configmap
TypeConfigMap = "configmap"
)
// ToConfigMap will get the data of the store and upload to configmap.
// Serverside Application controller can only use the config in appfile context by configmap.
func ToConfigMap(s Store, name, envName string, configData map[string]string) (*v1.ConfigMap, error) {
namespace, err := s.Namespace(envName)
if err != nil {
return nil, err
}
var cm = v1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
}
cm.SetName(name)
cm.SetNamespace(namespace)
cm.Data = configData
return &cm, nil
}
// GenConfigMapName is a fixed way to name the configmap name for appfile config
func GenConfigMapName(appName, serviceName, configName string) string {
return strings.Join([]string{"kubevela", appName, serviceName, configName}, Splitter)
}
var _ Store = &Configmap{}
// Configmap is the configmap implementation of config store
type Configmap struct {
Client client.Client
}
// GetConfigData will get config data from configmap
func (f *Configmap) GetConfigData(name, envName string) ([]map[string]string, error) {
namespace, err := f.Namespace(envName)
if err != nil {
return nil, err
}
var cm v1.ConfigMap
err = f.Client.Get(context.Background(), client.ObjectKey{Name: name, Namespace: namespace}, &cm)
if err != nil {
return nil, err
}
var data []map[string]string
for k, v := range cm.Data {
data = append(data, EncodeConfigFormat(k, v))
}
return data, nil
}
// Namespace returns the namespace of the config store from env
func (f *Configmap) Namespace(envName string) (string, error) {
// TODO(wonderflow): now we regard env as namespace, it should be fixed when env is store serverside as configmap
return envName, nil
}
// Type returns the type of the config store
func (Configmap) Type() string {
return TypeConfigMap
}

View File

@@ -1,65 +0,0 @@
/*
Copyright 2021 The KubeVela 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 config
import (
"bufio"
"bytes"
"github.com/oam-dev/kubevela/pkg/utils/config"
env2 "github.com/oam-dev/kubevela/pkg/utils/env"
)
// TypeLocal defines the local config store type
const TypeLocal = "local"
// Local is the local implementation of config store
type Local struct{}
var _ Store = &Local{}
// GetConfigData will return config data from local
func (l *Local) GetConfigData(configName, envName string) ([]map[string]string, error) {
cfgData, err := config.ReadConfig(envName, configName)
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(bytes.NewReader(cfgData))
var data []map[string]string
for scanner.Scan() {
k, v, err := config.ReadConfigLine(scanner.Text())
if err != nil {
return nil, err
}
data = append(data, EncodeConfigFormat(k, v))
}
return data, nil
}
// Namespace return namespace from env
func (l *Local) Namespace(envName string) (string, error) {
env, err := env2.GetEnvByName(envName)
if err != nil {
return "", err
}
return env.Namespace, nil
}
// Type returns the type of this config store implementation
func (l *Local) Type() string {
return TypeLocal
}

View File

@@ -1,76 +0,0 @@
/*
Copyright 2021 The KubeVela 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 config
import "errors"
// Store will get config data
type Store interface {
GetConfigData(configName, envName string) ([]map[string]string, error)
Type() string
Namespace(envName string) (string, error)
}
// TypeFake is a fake type
const TypeFake = "fake"
var _ Store = &Fake{}
// Fake is a fake implementation of config store, help for test
type Fake struct {
Data []map[string]string
}
// GetConfigData get data
func (f *Fake) GetConfigData(_ string, _ string) ([]map[string]string, error) {
return f.Data, nil
}
// Type return the type
func (Fake) Type() string {
return TypeFake
}
// Namespace return the Namespace
func (Fake) Namespace(_ string) (string, error) {
return "", nil
}
// EncodeConfigFormat will encode key-value to config{name: key, value: value} format
func EncodeConfigFormat(key, value string) map[string]string {
return map[string]string{
"name": key,
"value": value,
}
}
// DecodeConfigFormat will decode config{name: key, value: value} format to key-value mode.
func DecodeConfigFormat(data []map[string]string) (map[string]string, error) {
var res = make(map[string]string)
for _, d := range data {
key, ok := d["name"]
if !ok {
return nil, errors.New("invalid data format, no 'name' found")
}
value, ok := d["value"]
if !ok {
return nil, errors.New("invalid data format, no 'value' found")
}
res[key] = value
}
return res, nil
}

View File

@@ -91,6 +91,23 @@ func InitBaseRestConfig() (Args, error) {
}, nil
}
// globalClient will be a client for whole command lifecycle
var globalClient client.Client
// SetGlobalClient will set a client for one cli command
func SetGlobalClient(clt client.Client) error {
globalClient = clt
return nil
}
// GetClient will K8s client in args
func GetClient() (client.Client, error) {
if globalClient != nil {
return globalClient, nil
}
return nil, errors.New("client not set, call SetGlobalClient first")
}
// HTTPGet will send GET http request with context
func HTTPGet(ctx context.Context, url string) ([]byte, error) {
// Change NewRequest to NewRequestWithContext and pass context it

View File

@@ -1,84 +0,0 @@
/*
Copyright 2021 The KubeVela 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 config
import (
b64 "encoding/base64"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/oam-dev/kubevela/pkg/utils/env"
)
// ReadConfigLine will read config from line
func ReadConfigLine(line string) (string, string, error) {
ss := strings.SplitN(line, ":", 2)
if len(ss) != 2 {
return "", "", fmt.Errorf("config data is malformed: %s", line)
}
for i := range ss {
ss[i] = strings.TrimSpace(ss[i])
}
vDec, err := b64.StdEncoding.DecodeString(ss[1])
if err != nil {
return "", "", err
}
return ss[0], string(vDec), nil
}
// GetConfigsDir will get config from dir
func GetConfigsDir(envName string) (string, error) {
cfgDir := filepath.Join(env.GetEnvDirByName(envName), "configs")
err := os.MkdirAll(cfgDir, 0700)
return cfgDir, err
}
// DeleteConfig will delete local config file
func DeleteConfig(envName, configName string) error {
d, err := GetConfigsDir(envName)
if err != nil {
return err
}
cfgFile := filepath.Join(d, configName)
return os.RemoveAll(cfgFile)
}
// ReadConfig will read the config data from local
func ReadConfig(envName, configName string) ([]byte, error) {
d, err := GetConfigsDir(envName)
if err != nil {
return nil, err
}
cfgFile := filepath.Join(d, configName)
b, err := os.ReadFile(filepath.Clean(cfgFile))
if os.IsNotExist(err) {
return []byte{}, nil
}
return b, err
}
// WriteConfig will write data into local config
func WriteConfig(envName, configName string, data []byte) error {
d, err := GetConfigsDir(envName)
if err != nil {
return err
}
cfgFile := filepath.Join(d, configName)
return os.WriteFile(cfgFile, data, 0600)
}

374
pkg/utils/env/env.go vendored
View File

@@ -18,151 +18,150 @@ package env
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"cuelang.org/go/pkg/strings"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/apply"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/system"
)
// GetEnvDirByName will get env dir from name
func GetEnvDirByName(name string) string {
envdir, _ := system.GetEnvDir()
return filepath.Join(envdir, name)
const (
// IndicatingLabel is label key indicating application is an env
IndicatingLabel = "cli.env.oam.dev/name"
// RawType is component type of raw
RawType = "raw"
// DefaultEnvName is name of default env
DefaultEnvName = "default"
// DefaultEnvNamespace is namespace of default env
DefaultEnvNamespace = "default"
// AppNameSchema is used to generate env app name
AppNameSchema = "vela-env-%s"
// AppNamePrefix is prefix of AppNameSchema
AppNamePrefix = "vela-env-"
)
// app2Env and env2App are helper convert functions
func app2Env(app *v1beta1.Application) (*types.EnvMeta, error) {
namespace := getEnvNamespace(app)
env := types.EnvMeta{
Name: strings.Replace(app.Name, AppNamePrefix, "", 1),
Namespace: namespace,
Current: "",
}
return &env, nil
}
func env2App(meta *types.EnvMeta) *v1beta1.Application {
app := v1beta1.Application{
TypeMeta: metav1.TypeMeta{
Kind: v1beta1.ApplicationKind,
APIVersion: v1beta1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf(AppNameSchema, meta.Name),
Namespace: types.DefaultKubeVelaNS,
},
Spec: v1beta1.ApplicationSpec{
Components: []common2.ApplicationComponent{},
},
}
addNamespaceObjectIfNeeded(meta, &app)
labels := map[string]string{
IndicatingLabel: meta.Name,
}
app.SetLabels(labels)
return &app
}
// getEnvAppByName and deleteAppByName are application operation helper functions
func getEnvAppByName(envName string) (*v1beta1.Application, error) {
list, err := getEnvAppList()
if err != nil {
return nil, err
}
// match envName
for _, app := range list {
if app.Name == fmt.Sprintf(AppNameSchema, envName) {
return &app, nil
}
}
if envName == DefaultEnvName {
_ = initDefaultEnv()
return getEnvAppByName(envName)
}
return nil, errors.Errorf("application %s not found", envName)
}
func deleteAppByName(name string) error {
clt, err := common.GetClient()
if err != nil {
return errors.Wrap(err, "get client fail")
}
app, err := getEnvAppByName(name)
if err != nil {
return err
}
err = clt.Delete(context.Background(), app)
if err != nil {
return err
}
return nil
}
// following functions are CRUD of env
// CreateEnv will create e env.
// Because Env equals to namespace, one env should not be updated
func CreateEnv(envName string, envArgs *types.EnvMeta) error {
c, err := common.GetClient()
if err != nil {
return err
}
e := envArgs
nowEnv, err := GetEnvByName(envName)
if err == nil {
if nowEnv.Namespace != envArgs.Namespace {
return errors.Errorf("env %s has existed", envName)
}
return nil
}
if e.Namespace == "" {
e.Namespace = "default"
}
app := env2App(envArgs)
err = applyApp(context.TODO(), app, c)
if err != nil {
return err
}
return nil
}
// GetEnvByName will get env info by name
func GetEnvByName(name string) (*types.EnvMeta, error) {
data, err := os.ReadFile(filepath.Join(GetEnvDirByName(name), system.EnvConfigName))
init, err := getEnvAppByName(name)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("env %s not exist", name)
}
return nil, err
return nil, errors.Wrap(err, "Env not exist")
}
var meta types.EnvMeta
if err = json.Unmarshal(data, &meta); err != nil {
return nil, err
}
return &meta, nil
}
// CreateOrUpdateEnv will create or update env.
// If it does not exist, create it and set to the new env.
// If it exists, update it and set to the new env.
func CreateOrUpdateEnv(ctx context.Context, c client.Client, envName string, envArgs *types.EnvMeta) (string, error) {
createOrUpdated := "created"
old, err := GetEnvByName(envName)
if err == nil {
createOrUpdated = "updated"
if envArgs.Domain == "" {
envArgs.Domain = old.Domain
}
if envArgs.Email == "" {
envArgs.Email = old.Email
}
if envArgs.Namespace == "" {
envArgs.Namespace = old.Namespace
}
}
if envArgs.Namespace == "" {
envArgs.Namespace = "default"
}
var message = ""
// Check If Namespace Exists
if err := c.Get(ctx, k8stypes.NamespacedName{Name: envArgs.Namespace}, &corev1.Namespace{}); err != nil {
if !apierrors.IsNotFound(err) {
return message, err
}
// Create Namespace if not found
if err := c.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: envArgs.Namespace}}); err != nil {
return message, err
}
}
data, err := json.Marshal(envArgs)
if err != nil {
return message, err
}
envdir, err := system.GetEnvDir()
if err != nil {
return message, err
}
subEnvDir := filepath.Join(envdir, envName)
if _, err = system.CreateIfNotExist(subEnvDir); err != nil {
return message, err
}
// nolint:gosec
if err = os.WriteFile(filepath.Join(subEnvDir, system.EnvConfigName), data, 0644); err != nil {
return message, err
}
curEnvPath, err := system.GetCurrentEnvPath()
if err != nil {
return message, err
}
// nolint:gosec
if err = os.WriteFile(curEnvPath, []byte(envName), 0644); err != nil {
return message, err
}
message = fmt.Sprintf("environment %s %s, Namespace: %s", envName, createOrUpdated, envArgs.Namespace)
if envArgs.Email != "" {
message += fmt.Sprintf(", Email: %s", envArgs.Email)
}
return message, nil
}
// CreateEnv will only create. If env already exists, return error
func CreateEnv(ctx context.Context, c client.Client, envName string, envArgs *types.EnvMeta) (string, error) {
_, err := GetEnvByName(envName)
if err == nil {
message := fmt.Sprintf("Env %s already exist", envName)
return message, errors.New(message)
}
return CreateOrUpdateEnv(ctx, c, envName, envArgs)
}
// UpdateEnv will update Env, if env does not exist, return error
func UpdateEnv(ctx context.Context, c client.Client, envName string, namespace string) (string, error) {
var message = ""
envMeta, err := GetEnvByName(envName)
if err != nil {
return err.Error(), err
}
if err := c.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: envMeta.Namespace}}); err != nil && !apierrors.IsAlreadyExists(err) {
return message, err
}
envMeta.Namespace = namespace
data, err := json.Marshal(envMeta)
if err != nil {
return message, err
}
envdir, err := system.GetEnvDir()
if err != nil {
return message, err
}
subEnvDir := filepath.Join(envdir, envName)
// nolint:gosec
if err = os.WriteFile(filepath.Join(subEnvDir, system.EnvConfigName), data, 0644); err != nil {
return message, err
}
message = "Update env succeed"
return message, err
return app2Env(init)
}
// ListEnvs will list all envs
// if envName specified, return list that only contains one env
func ListEnvs(envName string) ([]*types.EnvMeta, error) {
var envList []*types.EnvMeta
if envName != "" {
@@ -176,40 +175,59 @@ func ListEnvs(envName string) ([]*types.EnvMeta, error) {
envList = append(envList, env)
return envList, err
}
envDir, err := system.GetEnvDir()
apps, err := getEnvAppList()
if err != nil {
return envList, err
return nil, err
}
files, err := os.ReadDir(envDir)
if err != nil {
return envList, err
// if even one env is not exist, create a default env
if len(apps) == 0 {
err := initDefaultEnv()
if err != nil {
return nil, err
}
return ListEnvs(envName)
}
curEnv, err := GetCurrentEnvName()
curEnv, err := getCurrentEnvName()
if err != nil {
curEnv = types.DefaultEnvName
}
for _, f := range files {
if !f.IsDir() {
continue
}
data, err := os.ReadFile(filepath.Clean(filepath.Join(envDir, f.Name(), system.EnvConfigName)))
for i := range apps {
env, err := app2Env(&apps[i])
if err != nil {
fmt.Printf("error occurred while listing env:" + err.Error())
continue
}
var envMeta types.EnvMeta
if err = json.Unmarshal(data, &envMeta); err != nil {
continue
if curEnv == env.Name {
env.Current = "*"
}
if curEnv == f.Name() {
envMeta.Current = "*"
}
envList = append(envList, &envMeta)
envList = append(envList, env)
}
return envList, nil
}
// GetCurrentEnvName will get current env name
func GetCurrentEnvName() (string, error) {
// DeleteEnv will delete env and its application
func DeleteEnv(envName string) (string, error) {
var message string
var err error
curEnv, err := getCurrentEnvName()
if err != nil {
return message, err
}
if envName == curEnv {
err = fmt.Errorf("you can't delete current using environment %s", curEnv)
return message, err
}
err = deleteAppByName(envName)
if err != nil {
return message, errors.Wrap(err, fmt.Sprintf("error deleting env %s", envName))
}
message = "env" + envName + " deleted"
return message, err
}
// getCurrentEnvName will get current env name
func getCurrentEnvName() (string, error) {
currentEnvPath, err := system.GetCurrentEnvPath()
if err != nil {
return "", err
@@ -221,34 +239,19 @@ func GetCurrentEnvName() (string, error) {
return string(data), nil
}
// DeleteEnv will delete env locally
func DeleteEnv(envName string) (string, error) {
var message string
var err error
curEnv, err := GetCurrentEnvName()
// GetCurrentEnv will get current env, create default env if not exist
func GetCurrentEnv() (*types.EnvMeta, error) {
envName, err := getCurrentEnvName()
if err != nil {
return message, err
}
if envName == curEnv {
err = fmt.Errorf("you can't delete current using environment %s", curEnv)
return message, err
}
envdir, err := system.GetEnvDir()
if err != nil {
return message, err
}
envPath := filepath.Join(envdir, envName)
if _, err := os.Stat(envPath); err != nil {
if os.IsNotExist(err) {
err = fmt.Errorf("%s does not exist", envName)
return message, err
if !os.IsNotExist(err) {
return nil, err
}
if err = initDefaultEnv(); err != nil {
return nil, err
}
envName = types.DefaultEnvName
}
if err = os.RemoveAll(envPath); err != nil {
return message, err
}
message = envName + " deleted"
return message, err
return GetEnvByName(envName)
}
// SetEnv will set the current env to the specified one
@@ -269,3 +272,34 @@ func SetEnv(envName string) (string, error) {
msg = fmt.Sprintf("Set environment succeed, current environment is " + envName + ", namespace is " + envMeta.Namespace)
return msg, nil
}
// applyApp helps apply app
func applyApp(ctx context.Context, app *v1beta1.Application, c client.Client) error {
applicator := apply.NewAPIApplicator(c)
err := applicator.Apply(ctx, app)
return err
}
// initDefaultEnv create default env if not exist
func initDefaultEnv() error {
fmt.Println("Initializing default vela env...")
defaultEnv := &types.EnvMeta{Name: DefaultEnvName, Namespace: DefaultEnvNamespace}
app := env2App(defaultEnv)
clt, err := common.GetClient()
if err != nil {
return err
}
err = applyApp(context.Background(), app, clt)
if err != nil {
return err
}
currentEnvPath, err := system.GetCurrentEnvPath()
if err != nil {
return err
}
//nolint:gosec
if err = os.WriteFile(currentEnvPath, []byte(DefaultEnvName), 0644); err != nil {
return err
}
return nil
}

92
pkg/utils/env/env_test.go vendored Normal file
View File

@@ -0,0 +1,92 @@
/*
Copyright 2021 The KubeVela 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 env
import (
"fmt"
"testing"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/apis/types"
)
func TestEnv(t *testing.T) {
testCases := []struct {
envName string
namespace string
wantComponents []common2.ApplicationComponent
labels map[string]string
}{
{
envName: "test-env",
namespace: "test-env-ns",
wantComponents: []common2.ApplicationComponent{
{
Name: "test-env-ns",
Type: RawType,
Properties: util.Object2RawExtension(map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]string{"name": "test-env-ns"},
})},
},
labels: map[string]string{IndicatingLabel: "test-env"},
},
{
envName: "test-env-default",
namespace: "default",
wantComponents: []common2.ApplicationComponent{},
labels: map[string]string{IndicatingLabel: "test-env-default"},
},
}
baseApp := v1beta1.Application{
TypeMeta: metav1.TypeMeta{
Kind: v1beta1.ApplicationKind,
APIVersion: v1beta1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "",
Namespace: types.DefaultKubeVelaNS,
},
Spec: v1beta1.ApplicationSpec{
Components: []common2.ApplicationComponent{}}}
for _, c := range testCases {
env := &types.EnvMeta{
Name: c.envName,
Namespace: c.namespace,
}
rightApp := baseApp.DeepCopy()
rightApp.ObjectMeta.Name = fmt.Sprintf(AppNameSchema, c.envName)
rightApp.ObjectMeta.Labels = c.labels
rightApp.Spec.Components = c.wantComponents
generatedApp := env2App(env)
assert.DeepEqual(t, generatedApp, rightApp)
generatedEnv, err := app2Env(rightApp)
assert.NilError(t, err)
assert.DeepEqual(t, generatedEnv, env)
}
}

87
pkg/utils/env/helper.go vendored Normal file
View File

@@ -0,0 +1,87 @@
/*
Copyright 2021 The KubeVela 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 env
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
// getEnvAppList will filter application that represent an env
func getEnvAppList() ([]v1beta1.Application, error) {
list := v1beta1.ApplicationList{}
clt, err := common.GetClient()
if err != nil {
return nil, err
}
ctx := context.Background()
matchLabels := metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{{
Key: IndicatingLabel,
Operator: metav1.LabelSelectorOpExists,
}},
}
selector, err := metav1.LabelSelectorAsSelector(&matchLabels)
if err != nil {
return nil, err
}
err = clt.List(ctx, &list, &client.ListOptions{LabelSelector: selector})
if err != nil {
return nil, err
}
return list.Items, nil
}
func getEnvNamespace(application *v1beta1.Application) string {
namespace := DefaultEnvNamespace
for _, comp := range application.Spec.Components {
if comp.Type == RawType {
obj, err := util.RawExtension2Unstructured(&comp.Properties)
if err != nil {
return ""
}
return obj.GetName()
}
}
return namespace
}
// add namespace object if env namespace is not default
func addNamespaceObjectIfNeeded(meta *types.EnvMeta, app *v1beta1.Application) {
if meta.Namespace != DefaultEnvNamespace {
app.Spec.Components = append(app.Spec.Components, common2.ApplicationComponent{
Name: meta.Namespace,
Type: RawType,
Properties: util.Object2RawExtension(map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]string{
"name": meta.Namespace,
},
}),
})
}
}

View File

@@ -17,11 +17,10 @@ limitations under the License.
package system
import (
"encoding/json"
"os"
"path/filepath"
"github.com/oam-dev/kubevela/apis/types"
"github.com/pkg/errors"
)
const defaultVelaHome = ".vela"
@@ -35,14 +34,23 @@ const (
// GetVelaHomeDir return vela home dir
func GetVelaHomeDir() (string, error) {
var velaHome string
if custom := os.Getenv(VelaHomeEnv); custom != "" {
return custom, nil
velaHome = custom
} else {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
velaHome = filepath.Join(home, defaultVelaHome)
}
home, err := os.UserHomeDir()
if err != nil {
return "", err
if _, err := os.Stat(velaHome); err != nil && os.IsNotExist(err) {
err := os.MkdirAll(velaHome, 0750)
if err != nil {
return "", errors.Wrap(err, "error when create vela home directory")
}
}
return filepath.Join(home, defaultVelaHome), nil
return velaHome, nil
}
// GetDefaultFrontendDir return default vela frontend dir
@@ -81,22 +89,14 @@ func GetCapabilityDir() (string, error) {
return filepath.Join(home, "capabilities"), nil
}
// GetEnvDir return KubeVela environments dir
func GetEnvDir() (string, error) {
homedir, err := GetVelaHomeDir()
if err != nil {
return "", err
}
return filepath.Join(homedir, "envs"), nil
}
// GetCurrentEnvPath return current env config
func GetCurrentEnvPath() (string, error) {
homedir, err := GetVelaHomeDir()
if err != nil {
return "", err
}
return filepath.Join(homedir, "curenv"), nil
envPath := filepath.Join(homedir, "curenv")
return envPath, nil
}
// InitDirs create dir if not exits
@@ -107,9 +107,6 @@ func InitDirs() error {
if err := InitCapCenterDir(); err != nil {
return err
}
if err := InitDefaultEnv(); err != nil {
return err
}
return nil
}
@@ -133,39 +130,6 @@ func InitCapabilityDir() error {
return err
}
// EnvConfigName defines config
const EnvConfigName = "config.json"
// InitDefaultEnv create dir if not exits
func InitDefaultEnv() error {
envDir, err := GetEnvDir()
if err != nil {
return err
}
defaultEnvDir := filepath.Join(envDir, types.DefaultEnvName)
exist, err := CreateIfNotExist(defaultEnvDir)
if err != nil {
return err
}
if exist {
return nil
}
data, _ := json.Marshal(&types.EnvMeta{Namespace: types.DefaultAppNamespace, Name: types.DefaultEnvName})
//nolint:gosec
if err = os.WriteFile(filepath.Join(defaultEnvDir, EnvConfigName), data, 0644); err != nil {
return err
}
curEnvPath, err := GetCurrentEnvPath()
if err != nil {
return err
}
//nolint:gosec
if err = os.WriteFile(curEnvPath, []byte(types.DefaultEnvName), 0644); err != nil {
return err
}
return nil
}
// CreateIfNotExist create dir if not exist
func CreateIfNotExist(dir string) (bool, error) {
_, err := os.Stat(dir)

View File

@@ -32,7 +32,6 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile/config"
"github.com/oam-dev/kubevela/pkg/builtin"
"github.com/oam-dev/kubevela/pkg/oam"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
@@ -64,16 +63,14 @@ type AppFile struct {
Services map[string]Service `json:"services"`
Secrets map[string]string `json:"secrets,omitempty"`
configGetter config.Store
initialized bool
initialized bool
}
// NewAppFile init an empty AppFile struct
func NewAppFile() *AppFile {
return &AppFile{
Services: make(map[string]Service),
Secrets: make(map[string]string),
configGetter: &config.Local{},
Services: make(map[string]Service),
Secrets: make(map[string]string),
}
}
@@ -158,26 +155,10 @@ func (app *AppFile) BuildOAMApplication(env *types.EnvMeta, io cmdutil.IOStreams
servApp.SetNamespace(env.Namespace)
servApp.SetName(app.Name)
servApp.Spec.Components = []common.ApplicationComponent{}
if !silence {
io.Infof("parsing application components")
}
for serviceName, svc := range app.GetServices() {
if !silence {
io.Infof("\nRendering configs for service (%s)...\n", serviceName)
}
configname := svc.GetUserConfigName()
if configname != "" {
configData, err := app.configGetter.GetConfigData(configname, env.Name)
if err != nil {
return nil, nil, err
}
decodedData, err := config.DecodeConfigFormat(configData)
if err != nil {
return nil, nil, err
}
cm, err := config.ToConfigMap(app.configGetter, config.GenConfigMapName(app.Name, serviceName, configname), env.Name, decodedData)
if err != nil {
return nil, nil, err
}
auxiliaryObjects = append(auxiliaryObjects, cm)
}
comp, err := svc.RenderServiceToApplicationComponent(tm, serviceName)
if err != nil {
return nil, nil, err

View File

@@ -30,7 +30,6 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile/config"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
@@ -137,7 +136,7 @@ func TestBuildOAMApplication2(t *testing.T) {
In: os.Stdin,
Out: os.Stdout,
}, tm, false)
assert.Equal(t, nil, err)
assert.NoError(t, err)
assert.Equal(t, tcase.expectApp, o)
}
}
@@ -168,19 +167,6 @@ services:
cmd: ["node", "server.js"]
`
yamlWithConfig := `name: myapp
services:
express-server:
type: withconfig
image: oamdev/testapp:v1
cmd: ["node", "server.js"]
route:
domain: example.com
http:
"/": 8080
config: test
`
templateWebservice := `parameter: #webservice
#webservice: {
cmd: [...string]
@@ -216,25 +202,6 @@ output: {
command: parameter.cmd
}
}`
templateWithConfig := `parameter: #withconfig
#withconfig: {
cmd: [...string]
image: string
}
output: {
apiVersion: "test.oam.dev/v1"
kind: "WebService"
metadata: {
name: context.name
}
spec: {
image: parameter.image
command: parameter.cmd
env: context.config
}
}
`
templateRoute := `parameter: #route
#route: {
domain: string
@@ -324,22 +291,6 @@ outputs: ingress: {
// TODO application 那边补测试:
// 2. 1对多的情况多对1 的情况
fakeConfigData2 := []map[string]string{{
"name": "test",
"value": "test-value",
}}
ac3cm := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
Data: map[string]string{
"test": "test-value",
},
}
ac3cm.SetName("kubevela-myapp-express-server-test")
health := &v1alpha2.HealthScope{
TypeMeta: metav1.TypeMeta{
APIVersion: v1alpha2.HealthScopeGroupVersionKind.GroupVersion().String(),
@@ -402,21 +353,6 @@ outputs: ingress: {
err: ErrImageNotDefined,
},
},
"config data should be set, add return configmap objects": {
args: args{
appfileData: yamlWithConfig,
workloadTemplates: map[string]string{
"withconfig": templateWithConfig,
},
traitTemplates: map[string]string{
"route": templateRoute,
},
},
want: want{
app: ac3,
objs: []oam.Object{ac3cm, health},
},
},
}
io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
@@ -424,9 +360,6 @@ outputs: ingress: {
t.Run(caseName, func(t *testing.T) {
app := NewAppFile()
app.configGetter = &config.Fake{
Data: fakeConfigData2,
}
err := yaml.Unmarshal([]byte(c.args.appfileData), app)
if err != nil {
t.Fatal(err)

View File

@@ -84,16 +84,6 @@ func LoadApplication(namespace, appName string, c common.Args) (*v1beta1.Applica
return app, nil
}
// Delete will delete an app along with it's appfile.
func Delete(envName, appName string) error {
return GetStorage().Delete(envName, appName)
}
// Save will save appfile into default dir.
func Save(app *api.Application, envName string) error {
return GetStorage().Save(app, envName)
}
// GetComponents will get oam components from Appfile.
func GetComponents(app *v1beta1.Application) []string {
var components []string

View File

@@ -1,65 +0,0 @@
/*
Copyright 2021 The KubeVela 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 driver
import (
"errors"
"github.com/oam-dev/kubevela/references/appfile/api"
)
// ConfigMapDriverName is local storage driver name
const ConfigMapDriverName = "ConfigMap"
// ConfigMap Storage
type ConfigMap struct {
api.Driver
}
// NewConfigMapStorage get storage client of ConfigMap type
func NewConfigMapStorage() *ConfigMap {
return &ConfigMap{}
}
// Name of local storage
func (c *ConfigMap) Name() string {
return ConfigMapDriverName
}
// List applications from configmap storage
func (c *ConfigMap) List(envName string) ([]*api.Application, error) {
// TODO support configmap storage
return nil, errors.New("not implement")
}
// Save applications from configmap storage
func (c *ConfigMap) Save(app *api.Application, envName string) error {
// TODO support configmap storage
return errors.New("not implement")
}
// Delete applications from configmap storage
func (c *ConfigMap) Delete(envName, appName string) error {
// TODO support configmap storage
return errors.New("not implement")
}
// Get applications from configmap storage
func (c *ConfigMap) Get(envName, appName string) (*api.Application, error) {
// TODO support configmap storage
return nil, errors.New("not implement")
}

View File

@@ -1,140 +0,0 @@
/*
Copyright 2021 The KubeVela 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 driver
import (
"reflect"
"testing"
"github.com/oam-dev/kubevela/references/appfile/api"
)
func TestConfigMapDelete(t *testing.T) {
type args struct {
envName string
appName string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &ConfigMap{}
if err := c.Delete(tt.args.envName, tt.args.appName); (err != nil) != tt.wantErr {
t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestConfigMapGet(t *testing.T) {
type args struct {
envName string
appName string
}
tests := []struct {
name string
args args
want *api.Application
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &ConfigMap{}
got, err := c.Get(tt.args.envName, tt.args.appName)
if (err != nil) != tt.wantErr {
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() got = %v, want %v", got, tt.want)
}
})
}
}
func TestConfigMapList(t *testing.T) {
type args struct {
envName string
}
tests := []struct {
name string
args args
want []*api.Application
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &ConfigMap{}
got, err := c.List(tt.args.envName)
if (err != nil) != tt.wantErr {
t.Errorf("List() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("List() got = %v, want %v", got, tt.want)
}
})
}
}
func TestConfigMapName(t *testing.T) {
tests := []struct {
name string
want string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &ConfigMap{}
if got := c.Name(); got != tt.want {
t.Errorf("Name() = %v, want %v", got, tt.want)
}
})
}
}
func TestConfigMapSave(t *testing.T) {
type args struct {
app *api.Application
envName string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &ConfigMap{}
if err := c.Save(tt.args.app, tt.args.envName); (err != nil) != tt.wantErr {
t.Errorf("Save() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -1,84 +0,0 @@
/*
Copyright 2021 The KubeVela 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 driver
import (
"fmt"
"os"
"path/filepath"
"time"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/pkg/utils/env"
"github.com/oam-dev/kubevela/pkg/utils/system"
"github.com/oam-dev/kubevela/references/appfile/api"
)
// LocalDriverName is local storage driver name
const LocalDriverName = "Local"
// Local Storage
type Local struct {
api.Driver
}
// NewLocalStorage get storage client of Local type
func NewLocalStorage() *Local {
return &Local{}
}
// Name is local storage driver name
func (l *Local) Name() string {
return LocalDriverName
}
// Save application from local storage
func (l *Local) Save(app *api.Application, envName string) error {
appDir, err := getApplicationDir(envName)
if err != nil {
return err
}
if app.CreateTime.IsZero() {
app.CreateTime = time.Now()
}
app.UpdateTime = time.Now()
out, err := yaml.Marshal(app)
if err != nil {
return err
}
//nolint:gosec
return os.WriteFile(filepath.Join(appDir, app.Name+".yaml"), out, 0644)
}
// Delete application from local storage
func (l *Local) Delete(envName, appName string) error {
appDir, err := getApplicationDir(envName)
if err != nil {
return err
}
return os.Remove(filepath.Join(appDir, appName+".yaml"))
}
func getApplicationDir(envName string) (string, error) {
appDir := filepath.Join(env.GetEnvDirByName(envName), "applications")
_, err := system.CreateIfNotExist(appDir)
if err != nil {
err = fmt.Errorf("getting application directory from env %s failed, error: %w ", envName, err)
}
return appDir, err
}

View File

@@ -1,155 +0,0 @@
/*
Copyright 2021 The KubeVela 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 driver
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/references/appfile/api"
)
var dir string
var afile *api.AppFile
var appName = "testsvc"
var envName = "default"
func init() {
dir, _ = getApplicationDir(envName)
afile = api.NewAppFile()
afile.Name = appName
svcs := make(map[string]api.Service)
svcs["wordpress"] = map[string]interface{}{
"type": "webservice",
"image": "wordpress:php7.4-apache",
"port": "80",
"cpu": "1",
"route": "",
}
afile.Services = svcs
out, _ := yaml.Marshal(afile)
_ = os.WriteFile(filepath.Join(dir, appName+".yaml"), out, 0644)
}
func TestLocalDelete(t *testing.T) {
type args struct {
envName string
appName string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"testLocalDelete1", args{envName: envName, appName: appName}, false},
{"testLocalDelete2", args{envName: envName, appName: "test"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &Local{}
if err := l.Delete(tt.args.envName, tt.args.appName); (err != nil) != tt.wantErr {
t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestLocalSave(t *testing.T) {
type args struct {
app *api.Application
envName string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"TestLocal_Save1", args{&api.Application{AppFile: afile, Tm: nil}, envName}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &Local{}
if err := l.Save(tt.args.app, tt.args.envName); (err != nil) != tt.wantErr {
t.Errorf("Save() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestLocalName(t *testing.T) {
tests := []struct {
name string
want string
}{
{"testDriverName", "Local"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &Local{}
if got := l.Name(); got != tt.want {
t.Errorf("Name() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewLocalStorage(t *testing.T) {
tests := []struct {
name string
want *Local
}{
{"testNewLocal", &Local{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewLocalStorage(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewLocalStorage() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetApplicationDir(t *testing.T) {
type args struct {
envName string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{"testGetApplicationDir", args{envName: envName}, "/envs/default/applications", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getApplicationDir(tt.args.envName)
if (err != nil) != tt.wantErr {
t.Errorf("getApplicationDir() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !strings.Contains(got, tt.want) {
t.Errorf("getApplicationDir() got = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,60 +0,0 @@
/*
Copyright 2021 The KubeVela 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 appfile
import (
"os"
"github.com/oam-dev/kubevela/pkg/utils/system"
"github.com/oam-dev/kubevela/references/appfile/api"
"github.com/oam-dev/kubevela/references/appfile/driver"
)
// Store application store client
var store *Storage
// Storage is common storage clientuse it to get app and others resource
type Storage struct {
api.Driver
}
// GetStorage will create storage driver from the system environment of "STORAGE_DRIVER"
func GetStorage() *Storage {
driverName := os.Getenv(system.StorageDriverEnv)
if store == nil || store.Name() != driverName {
switch driverName {
// TODO mutli implement Storage
case driver.ConfigMapDriverName:
store = &Storage{driver.NewConfigMapStorage()}
case driver.LocalDriverName:
store = &Storage{driver.NewLocalStorage()}
default:
store = &Storage{driver.NewLocalStorage()}
}
}
return store
}
// Save application storage common implement
func (s *Storage) Save(app *api.Application, envName string) error {
return s.Driver.Save(app, envName)
}
// Delete application storage common implement
func (s *Storage) Delete(envName, appName string) error {
return s.Driver.Delete(envName, appName)
}

View File

@@ -1,44 +0,0 @@
/*
Copyright 2021 The KubeVela 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 appfile
import (
"os"
"testing"
"github.com/oam-dev/kubevela/pkg/utils/system"
"github.com/oam-dev/kubevela/references/appfile/driver"
)
func TestGetStorage(t *testing.T) {
_ = os.Setenv(system.StorageDriverEnv, driver.ConfigMapDriverName)
store := &Storage{driver.NewConfigMapStorage()}
tests := []struct {
name string
want *Storage
}{
{name: "TestGetStorage_ConfigMap", want: store},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetStorage(); got.Name() != tt.want.Name() {
t.Errorf("GetStorage() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -154,7 +154,7 @@ func NewCapUninstallCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.
}
name = l[1]
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -199,7 +199,7 @@ func NewCapListCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Comma
if len(args) > 0 {
repoName = args[0]
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -87,7 +87,6 @@ func NewCommand() *cobra.Command {
NewPortForwardCommand(commandArgs, ioStream),
NewLogsCommand(commandArgs, ioStream),
NewEnvCommand(commandArgs, ioStream),
NewConfigCommand(ioStream),
// Workflows
NewWorkflowCommand(commandArgs, ioStream),

View File

@@ -51,7 +51,7 @@ func NewComponentsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Co
},
RunE: func(cmd *cobra.Command, args []string) error {
isDiscover, _ := cmd.Flags().GetBool("discover")
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -1,274 +0,0 @@
/*
Copyright 2021 The KubeVela 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 cli
import (
"bufio"
"bytes"
b64 "encoding/base64"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/config"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
)
// Notes about config dir layout:
// Under each env dir, there are individual files for each config.
// The format is the same as k8s Secret.Data field with value base64 encoded.
// NewConfigCommand will create command for config management for AppFile
func NewConfigCommand(io cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
DisableFlagsInUseLine: true,
Short: "Manage configurations",
Long: "Manage configurations",
Annotations: map[string]string{
types.TagCommandType: types.TypeApp,
},
}
cmd.SetOut(io.Out)
cmd.AddCommand(
NewConfigListCommand(io),
NewConfigGetCommand(io),
NewConfigSetCommand(io),
NewConfigDeleteCommand(io),
)
return cmd
}
// NewConfigListCommand list all created configs
func NewConfigListCommand(io cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
DisableFlagsInUseLine: true,
Short: "List configs",
Long: "List all configs",
Example: `vela config ls`,
RunE: func(cmd *cobra.Command, args []string) error {
return ListConfigs(io, cmd)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
cmd.SetOut(io.Out)
return cmd
}
func getConfigDir(cmd *cobra.Command) (string, error) {
e, err := GetEnv(cmd)
if err != nil {
return "", err
}
return config.GetConfigsDir(e.Name)
}
// ListConfigs will list all configs
func ListConfigs(ioStreams cmdutil.IOStreams, cmd *cobra.Command) error {
d, err := getConfigDir(cmd)
if err != nil {
return err
}
table := newUITable()
table.AddRow("NAME")
cfgList, err := listConfigs(d)
if err != nil {
return err
}
for _, name := range cfgList {
table.AddRow(name)
}
ioStreams.Info(table.String())
return nil
}
func listConfigs(dir string) ([]string, error) {
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
l := []string{}
for _, f := range files {
l = append(l, f.Name())
}
return l, nil
}
// NewConfigGetCommand get config from local
func NewConfigGetCommand(io cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Aliases: []string{"get"},
DisableFlagsInUseLine: true,
Short: "Get data for a config",
Long: "Get data for a config",
Example: `vela config get <config-name>`,
RunE: func(cmd *cobra.Command, args []string) error {
return getConfig(args, io, cmd)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
cmd.SetOut(io.Out)
return cmd
}
func getConfig(args []string, io cmdutil.IOStreams, cmd *cobra.Command) error {
e, err := GetEnv(cmd)
if err != nil {
return err
}
if len(args) < 1 {
return fmt.Errorf("must specify config name, vela config get <name>")
}
configName := args[0]
cfgData, err := config.ReadConfig(e.Name, configName)
if err != nil {
return err
}
io.Infof("Data:\n")
scanner := bufio.NewScanner(bytes.NewReader(cfgData))
for scanner.Scan() {
k, v, err := config.ReadConfigLine(scanner.Text())
if err != nil {
return err
}
io.Infof(" %s: %s\n", k, v)
}
return nil
}
// NewConfigSetCommand set a config data in local
func NewConfigSetCommand(io cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "set",
Aliases: []string{"set"},
DisableFlagsInUseLine: true,
Short: "Set data for a config",
Long: "Set data for a config",
Example: `vela config set <config-name> KEY=VALUE K2=V2`,
RunE: func(cmd *cobra.Command, args []string) error {
return setConfig(args, io, cmd)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
cmd.SetOut(io.Out)
return cmd
}
func setConfig(args []string, io cmdutil.IOStreams, cmd *cobra.Command) error {
e, err := GetEnv(cmd)
if err != nil {
return err
}
envName := e.Name
if len(args) < 1 {
return fmt.Errorf("must specify config name, vela config set <name> KEY=VALUE")
}
configName := args[0]
input := map[string]string{}
for _, arg := range args[1:] {
ss := strings.SplitN(arg, "=", 2)
if len(ss) != 2 {
return fmt.Errorf("KV argument malformed: %s, should be KEY=VALUE", arg)
}
k := strings.TrimSpace(ss[0])
v := strings.TrimSpace(ss[1])
if _, ok := input[k]; ok {
return fmt.Errorf("KEY is not unique: %s", arg)
}
input[k] = v
}
cfgData, err := config.ReadConfig(envName, configName)
if err != nil {
return err
}
io.Infof("reading existing config data and merging with user input\n")
scanner := bufio.NewScanner(bytes.NewReader(cfgData))
for scanner.Scan() {
k, v, err := config.ReadConfigLine(scanner.Text())
if err != nil {
return err
}
input[k] = v
}
var out bytes.Buffer
for k, v := range input {
vEnc := b64.StdEncoding.EncodeToString([]byte(v))
out.WriteString(fmt.Sprintf("%s: %s\n", k, vEnc))
}
err = config.WriteConfig(envName, configName, out.Bytes())
if err != nil {
return err
}
io.Infof("config data saved successfully %s\n", emojiSucceed)
return nil
}
// NewConfigDeleteCommand delete a config from local
func NewConfigDeleteCommand(io cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "del",
Aliases: []string{"del"},
DisableFlagsInUseLine: true,
Short: "Delete config",
Long: "Delete config",
Example: `vela config del <config-name>`,
RunE: func(cmd *cobra.Command, args []string) error {
return deleteConfig(args, io, cmd)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
cmd.SetOut(io.Out)
return cmd
}
func deleteConfig(args []string, io cmdutil.IOStreams, cmd *cobra.Command) error {
e, err := GetEnv(cmd)
if err != nil {
return err
}
if len(args) < 1 {
return fmt.Errorf("must specify config name, vela config get <name>")
}
configName := args[0]
err = config.DeleteConfig(e.Name, configName)
if err != nil {
return err
}
io.Infof("config (%s) deleted successfully\n", configName)
return nil
}

View File

@@ -1,90 +0,0 @@
/*
Copyright 2021 The KubeVela 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 cli
import (
"bytes"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/oam-dev/kubevela/pkg/utils/system"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
)
func TestConfigCommand(t *testing.T) {
// Set VELA_HOME to local
assert.NoError(t, os.Setenv(system.VelaHomeEnv, ".test_vela"))
home, err := system.GetVelaHomeDir()
assert.NoError(t, err)
assert.Equal(t, true, strings.HasSuffix(home, ".test_vela"))
defer os.RemoveAll(home)
// Create Default Env
err = system.InitDefaultEnv()
assert.NoError(t, err)
// vela config set test a=b
io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
err = setConfig([]string{"test", "a=b"}, io, nil)
if err != nil {
t.Fatal(err)
}
// vela config get test
var b bytes.Buffer
io.Out = &b
err = getConfig([]string{"test"}, io, nil)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "Data:\n a: b\n", b.String())
// vela config set test2 c=d
io.Out = os.Stdout
err = setConfig([]string{"test2", "c=d"}, io, nil)
if err != nil {
t.Fatal(err)
}
// vela config ls
b = bytes.Buffer{}
io.Out = &b
err = ListConfigs(io, nil)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "NAME \ntest \ntest2\n", b.String())
// vela config del test
io.Out = os.Stdout
err = deleteConfig([]string{"test"}, io, nil)
if err != nil {
t.Fatal(err)
}
// vela config ls
b = bytes.Buffer{}
io.Out = &b
err = ListConfigs(io, nil)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "NAME \ntest2\n", b.String())
}

View File

@@ -54,7 +54,7 @@ func NewDeleteCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Comman
C: c,
}
o.Client = newClient
o.Env, err = GetEnv(cmd)
o.Env, err = GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -72,7 +72,7 @@ func NewDryRunCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
velaEnv, err := GetEnv(cmd)
velaEnv, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -17,17 +17,14 @@ limitations under the License.
package cli
import (
"context"
"fmt"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/env"
"github.com/oam-dev/kubevela/pkg/utils/system"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
)
@@ -38,17 +35,20 @@ func NewEnvCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Command {
DisableFlagsInUseLine: true,
Short: "Manage environments",
Long: "Manage environments",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return c.SetConfig()
},
Annotations: map[string]string{
types.TagCommandType: types.TypeApp,
},
}
cmd.SetOut(ioStream.Out)
cmd.AddCommand(NewEnvListCommand(ioStream), NewEnvInitCommand(c, ioStream), NewEnvSetCommand(ioStream), NewEnvDeleteCommand(ioStream))
cmd.AddCommand(NewEnvListCommand(c, ioStream), NewEnvInitCommand(c, ioStream), NewEnvSetCommand(c, ioStream), NewEnvDeleteCommand(c, ioStream))
return cmd
}
// NewEnvListCommand creates `env list` command for listing all environments
func NewEnvListCommand(ioStream cmdutil.IOStreams) *cobra.Command {
func NewEnvListCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
@@ -57,6 +57,14 @@ func NewEnvListCommand(ioStream cmdutil.IOStreams) *cobra.Command {
Long: "List all environments",
Example: `vela env ls [env-name]`,
RunE: func(cmd *cobra.Command, args []string) error {
clt, err := c.GetClient()
if err != nil {
return err
}
err = common.SetGlobalClient(clt)
if err != nil {
return err
}
return ListEnvs(args, ioStream)
},
Annotations: map[string]string{
@@ -70,23 +78,22 @@ func NewEnvListCommand(ioStream cmdutil.IOStreams) *cobra.Command {
// NewEnvInitCommand creates `env init` command for initializing environments
func NewEnvInitCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
var envArgs types.EnvMeta
ctx := context.Background()
cmd := &cobra.Command{
Use: "init <envName>",
DisableFlagsInUseLine: true,
Short: "Create environments",
Long: "Create environment and set the currently using environment",
Example: `vela env init test --namespace test --email my@email.com`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return c.SetConfig()
},
Example: `vela env init test --namespace test`,
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := c.GetClient()
clt, err := c.GetClient()
if err != nil {
return err
}
return CreateOrUpdateEnv(ctx, newClient, &envArgs, args, ioStreams)
err = common.SetGlobalClient(clt)
if err != nil {
return err
}
return CreateEnv(&envArgs, args, ioStreams)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
@@ -94,14 +101,11 @@ func NewEnvInitCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Comman
}
cmd.SetOut(ioStreams.Out)
cmd.Flags().StringVar(&envArgs.Namespace, "namespace", "", "specify K8s namespace for env")
cmd.Flags().StringVar(&envArgs.Email, "email", "", "specify email for production TLS Certificate notification")
cmd.Flags().StringVar(&envArgs.Domain, "domain", "", "specify domain your applications")
return cmd
}
// NewEnvDeleteCommand creates `env delete` command for deleting environments
func NewEnvDeleteCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
func NewEnvDeleteCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
DisableFlagsInUseLine: true,
@@ -109,7 +113,15 @@ func NewEnvDeleteCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
Long: "Delete environment",
Example: `vela env delete test`,
RunE: func(cmd *cobra.Command, args []string) error {
return DeleteEnv(ctx, args, ioStreams)
clt, err := c.GetClient()
if err != nil {
return err
}
err = common.SetGlobalClient(clt)
if err != nil {
return err
}
return DeleteEnv(args, ioStreams)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
@@ -120,7 +132,7 @@ func NewEnvDeleteCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
}
// NewEnvSetCommand creates `env set` command for setting current environment
func NewEnvSetCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
func NewEnvSetCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "set",
Aliases: []string{"sw"},
@@ -129,6 +141,14 @@ func NewEnvSetCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
Long: "Set an environment as the current using one",
Example: `vela env set test`,
RunE: func(cmd *cobra.Command, args []string) error {
clt, err := c.GetClient()
if err != nil {
return err
}
err = common.SetGlobalClient(clt)
if err != nil {
return err
}
return SetEnv(args, ioStreams)
},
Annotations: map[string]string{
@@ -142,7 +162,7 @@ func NewEnvSetCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
// ListEnvs shows info of all environments
func ListEnvs(args []string, ioStreams cmdutil.IOStreams) error {
table := newUITable()
table.AddRow("NAME", "CURRENT", "NAMESPACE", "EMAIL", "DOMAIN")
table.AddRow("NAME", "NAMESPACE", "CURRENT")
var envName = ""
if len(args) > 0 {
envName = args[0]
@@ -152,14 +172,14 @@ func ListEnvs(args []string, ioStreams cmdutil.IOStreams) error {
return err
}
for _, env := range envList {
table.AddRow(env.Name, env.Current, env.Namespace, env.Email, env.Domain)
table.AddRow(env.Name, env.Namespace, env.Current)
}
ioStreams.Info(table.String())
return nil
}
// DeleteEnv deletes an environment
func DeleteEnv(ctx context.Context, args []string, ioStreams cmdutil.IOStreams) error {
func DeleteEnv(args []string, ioStreams cmdutil.IOStreams) error {
if len(args) < 1 {
return fmt.Errorf("you must specify environment name for 'vela env delete' command")
}
@@ -173,18 +193,18 @@ func DeleteEnv(ctx context.Context, args []string, ioStreams cmdutil.IOStreams)
return nil
}
// CreateOrUpdateEnv creates or updates an environment
func CreateOrUpdateEnv(ctx context.Context, c client.Client, envArgs *types.EnvMeta, args []string, ioStreams cmdutil.IOStreams) error {
// CreateEnv creates an environment
func CreateEnv(envArgs *types.EnvMeta, args []string, ioStreams cmdutil.IOStreams) error {
if len(args) < 1 {
return fmt.Errorf("you must specify environment name for 'vela env init' command")
}
envName := args[0]
envArgs.Name = envName
msg, err := env.CreateOrUpdateEnv(ctx, c, envName, envArgs)
err := env.CreateEnv(envName, envArgs)
if err != nil {
return err
}
ioStreams.Info(msg)
ioStreams.Infof("environment %s created\n", envName)
return nil
}
@@ -202,26 +222,23 @@ func SetEnv(args []string, ioStreams cmdutil.IOStreams) error {
return nil
}
// GetEnv gets environment by name or current environment
// GetFlagEnvOrCurrent gets environment by name or current environment
// if no env exists, then init default environment
func GetEnv(cmd *cobra.Command) (*types.EnvMeta, error) {
func GetFlagEnvOrCurrent(cmd *cobra.Command, args common.Args) (*types.EnvMeta, error) {
clt, err := args.GetClient()
if err != nil {
return nil, err
}
err = common.SetGlobalClient(clt)
if err != nil {
return nil, errors.Wrap(err, "get flag env fail")
}
var envName string
var err error
if cmd != nil {
envName = cmd.Flag("env").Value.String()
}
if envName != "" {
return env.GetEnvByName(envName)
}
envName, err = env.GetCurrentEnvName()
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
if err = system.InitDefaultEnv(); err != nil {
return nil, err
}
envName = types.DefaultEnvName
}
return env.GetEnvByName(envName)
return env.GetCurrentEnv()
}

View File

@@ -17,108 +17,19 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"os"
"strings"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/env"
"github.com/oam-dev/kubevela/pkg/utils/system"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
)
func TestENV(t *testing.T) {
ctx := context.Background()
assert.NoError(t, os.Setenv(system.VelaHomeEnv, ".test_vela"))
home, err := system.GetVelaHomeDir()
assert.NoError(t, err)
assert.Equal(t, true, strings.HasSuffix(home, ".test_vela"))
defer os.RemoveAll(home)
// Create Default Env
err = system.InitDefaultEnv()
assert.NoError(t, err)
// check and compare create default env success
curEnvName, err := env.GetCurrentEnvName()
assert.NoError(t, err)
assert.Equal(t, "default", curEnvName)
gotEnv, err := GetEnv(nil)
assert.NoError(t, err)
assert.Equal(t, &types.EnvMeta{
Namespace: "default",
Name: "default",
}, gotEnv)
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
exp := &types.EnvMeta{
Namespace: "test1",
Name: "env1",
}
client := test.NewMockClient()
// Create env1
err = CreateOrUpdateEnv(ctx, client, exp, []string{"env1"}, ioStream)
assert.NoError(t, err)
// check and compare create env success
curEnvName, err = env.GetCurrentEnvName()
assert.NoError(t, err)
assert.Equal(t, "env1", curEnvName)
gotEnv, err = GetEnv(nil)
assert.NoError(t, err)
assert.Equal(t, exp, gotEnv)
// List all env
var b bytes.Buffer
ioStream.Out = &b
err = ListEnvs([]string{}, ioStream)
assert.NoError(t, err)
assert.Equal(t, "NAME \tCURRENT\tNAMESPACE\tEMAIL\tDOMAIN\ndefault\t \tdefault \t \t \nenv1 \t* \ttest1 \t \t \n", b.String())
b.Reset()
err = ListEnvs([]string{"env1"}, ioStream)
assert.NoError(t, err)
assert.Equal(t, "NAME\tCURRENT\tNAMESPACE\tEMAIL\tDOMAIN\nenv1\t \ttest1 \t \t \n", b.String())
ioStream.Out = os.Stdout
// can not delete current env
err = DeleteEnv(ctx, []string{"env1"}, ioStream)
assert.Error(t, err)
// set as default env
err = SetEnv([]string{"default"}, ioStream)
assert.NoError(t, err)
// check env set success
gotEnv, err = GetEnv(nil)
assert.NoError(t, err)
assert.Equal(t, &types.EnvMeta{
Namespace: "default",
Name: "default",
}, gotEnv)
// delete env
err = DeleteEnv(ctx, []string{"env1"}, ioStream)
assert.NoError(t, err)
// can not set as a non-exist env
err = SetEnv([]string{"env1"}, ioStream)
assert.Error(t, err)
// set success
err = SetEnv([]string{"default"}, ioStream)
assert.NoError(t, err)
}
func TestEnvInitCommandPersistentPreRunE(t *testing.T) {
io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
fakeC := common.Args{}
cmd := NewEnvInitCommand(fakeC, io)
assert.Nil(t, cmd.PersistentPreRunE(new(cobra.Command), []string{}))
}

View File

@@ -133,7 +133,7 @@ func (o *VelaExecOptions) Init(ctx context.Context, c *cobra.Command, argsIn []s
o.Cmd = c
o.Args = argsIn
env, err := GetEnv(o.Cmd)
env, err := GetFlagEnvOrCurrent(o.Cmd, o.VelaC)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
// NewExportCommand will create command for exporting deploy manifests from an AppFile
func NewExportCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command {
appFilePath := new(string)
cmd := &cobra.Command{
Use: "export",
DisableFlagsInUseLine: true,
@@ -36,7 +37,7 @@ func NewExportCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command
types.TagCommandType: types.TypeStart,
},
RunE: func(cmd *cobra.Command, args []string) error {
velaEnv, err := GetEnv(cmd)
velaEnv, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -44,11 +45,7 @@ func NewExportCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command
IO: ioStream,
Env: velaEnv,
}
filePath, err := cmd.Flags().GetString(appFilePath)
if err != nil {
return err
}
_, data, err := o.Export(filePath, velaEnv.Namespace, true, c)
_, data, err := o.Export(*appFilePath, velaEnv.Namespace, true, c)
if err != nil {
return err
}
@@ -58,6 +55,6 @@ func NewExportCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command
}
cmd.SetOut(ioStream.Out)
cmd.Flags().StringP(appFilePath, "f", "", "specify file path for appfile")
cmd.Flags().StringVarP(appFilePath, "file", "f", "", "specify file path for appfile")
return cmd
}

View File

@@ -26,6 +26,7 @@ import (
"cuelang.org/go/cue"
"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -72,7 +73,7 @@ func NewInitCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Command
return err
}
o.client = newClient
o.Env, err = GetEnv(cmd)
o.Env, err = GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -146,27 +147,8 @@ func (o *appInitOptions) CheckEnv() error {
if o.Env.Namespace == "" {
o.Env.Namespace = "default"
}
o.Infof("Environment: %s, namespace: %s\n\n", o.Env.Name, o.Env.Namespace)
if o.Env.Domain == "" {
prompt := &survey.Input{
Message: "What is the domain of your application service (optional): ",
}
err := survey.AskOne(prompt, &o.Env.Domain)
if err != nil {
return fmt.Errorf("read domain err %w", err)
}
}
if o.Env.Email == "" {
prompt := &survey.Input{
Message: "What is your email (optional, used to generate certification): ",
}
err := survey.AskOne(prompt, &o.Env.Email)
if err != nil {
return fmt.Errorf("read email err %w", err)
}
}
if _, err := env.CreateOrUpdateEnv(context.Background(), o.client, o.Env.Name, o.Env); err != nil {
return err
if err := env.CreateEnv(o.Env.Name, o.Env); err != nil {
return errors.Wrap(err, "app init create namespace err")
}
return nil
}

View File

@@ -73,7 +73,7 @@ func NewLiveDiffCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Comma
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
velaEnv, err := GetEnv(cmd)
velaEnv, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -57,7 +57,7 @@ func NewLogsCommand(c common.Args, ioStreams util.IOStreams) *cobra.Command {
ioStreams.Errorf("please specify app name")
return nil
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -44,7 +44,7 @@ func NewListCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -124,7 +124,7 @@ func (o *VelaPortForwardOptions) Init(ctx context.Context, cmd *cobra.Command, a
o.Cmd = cmd
o.Args = argsIn
env, err := GetEnv(o.Cmd)
env, err := GetFlagEnvOrCurrent(o.Cmd, o.VelaC)
if err != nil {
return err
}

View File

@@ -74,7 +74,7 @@ func NewCapabilityShowCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra
}
ctx := context.Background()
capabilityName := args[0]
velaEnv, err := GetEnv(cmd)
velaEnv, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -106,7 +106,7 @@ func NewAppStatusCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Comm
os.Exit(1)
}
appName := args[0]
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
ioStreams.Errorf("Error: failed to get Env: %s", err)
return err

View File

@@ -50,7 +50,7 @@ func NewTraitsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Comman
},
RunE: func(cmd *cobra.Command, args []string) error {
isDiscover, _ := cmd.Flags().GetBool("discover")
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -31,12 +31,9 @@ import (
"github.com/oam-dev/kubevela/references/common"
)
var (
appFilePath string
)
// NewUpCommand will create command for applying an AppFile
func NewUpCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command {
appFilePath := new(string)
cmd := &cobra.Command{
Use: "up",
DisableFlagsInUseLine: true,
@@ -49,7 +46,7 @@ func NewUpCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command {
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
velaEnv, err := GetEnv(cmd)
velaEnv, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -57,11 +54,7 @@ func NewUpCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command {
if err != nil {
return err
}
filePath, err := cmd.Flags().GetString(appFilePath)
if err != nil {
return err
}
fileContent, err := os.ReadFile(filepath.Clean(filePath))
fileContent, err := os.ReadFile(filepath.Clean(*appFilePath))
if err != nil {
return err
}
@@ -81,12 +74,12 @@ func NewUpCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command {
IO: ioStream,
Env: velaEnv,
}
return o.Run(filePath, velaEnv.Namespace, c)
return o.Run(*appFilePath, velaEnv.Namespace, c)
}
return nil
},
}
cmd.SetOut(ioStream.Out)
cmd.Flags().StringP(appFilePath, "f", "", "specify file path for appfile")
cmd.Flags().StringVarP(appFilePath, "file", "f", "", "specify file path for appfile")
return cmd
}

View File

@@ -60,7 +60,7 @@ func NewWorkflowSuspendCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra
if len(args) < 1 {
return fmt.Errorf("must specify application name")
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -99,7 +99,7 @@ func NewWorkflowResumeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.
if len(args) < 1 {
return fmt.Errorf("must specify application name")
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -148,7 +148,7 @@ func NewWorkflowTerminateCommand(c common.Args, ioStream cmdutil.IOStreams) *cob
if len(args) < 1 {
return fmt.Errorf("must specify application name")
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
@@ -187,7 +187,7 @@ func NewWorkflowRestartCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra
if len(args) < 1 {
return fmt.Errorf("must specify application name")
}
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -38,7 +38,7 @@ func NewWorkloadsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Com
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
env, err := GetEnv(cmd)
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}

View File

@@ -209,9 +209,6 @@ func RetrieveApplicationStatusByName(ctx context.Context, c client.Reader, appli
// DeleteApp will delete app including server side
func (o *DeleteOptions) DeleteApp() (string, error) {
if err := appfile.Delete(o.Env.Name, o.AppName); err != nil && !os.IsNotExist(err) {
return "", err
}
ctx := context.Background()
var app = new(corev1beta1.Application)
err := o.Client.Get(ctx, client.ObjectKey{Name: o.AppName, Namespace: o.Env.Namespace}, app)
@@ -443,9 +440,6 @@ func (o *AppfileOptions) Run(filePath, namespace string, c common.Args) error {
// BaseAppFileRun starts an application according to Appfile
func (o *AppfileOptions) BaseAppFileRun(result *BuildResult, args common.Args) error {
if err := o.saveToAppDir(result.appFile); err != nil {
return errors.Wrap(err, "save to app dir failed")
}
kubernetesComponent, err := appfile.ApplyTerraform(result.application, o.Kubecli, o.IO, o.Env.Namespace, args)
if err != nil {
@@ -457,11 +451,6 @@ func (o *AppfileOptions) BaseAppFileRun(result *BuildResult, args common.Args) e
return o.ApplyApp(result.application, result.scopes)
}
func (o *AppfileOptions) saveToAppDir(f *api.AppFile) error {
app := &api.Application{AppFile: f}
return appfile.Save(app, o.Env.Name)
}
// ApplyApp applys config resources for the app.
// It differs by create and update:
// - for create, it displays app status along with information of url, metrics, ssh, logging.

View File

@@ -139,5 +139,5 @@ func BaseComplete(env *types.EnvMeta, c common.Args, workloadName string, appNam
if err = appfile.SetWorkload(app, workloadName, tp, workloadData); err != nil {
return app, err
}
return app, appfile.Save(app, env.Name)
return app, nil
}