Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update the workspace storage definition and implementation #941

Merged
merged 1 commit into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ type Backend interface {
// todo: add functions to parse storage for spec, the format is like the following:
// SpecStorage(projectName, stackName string) spec.Storage

// WorkspaceStorage returns the workspace storage and init default workspace.
WorkspaceStorage() (workspace.Storage, error)

// StateStorage returns the state storage.
StateStorage(project, stack, workspace string) state.Storage
}

// NewBackend creates the Backend with the configuration set in the Kusion configuration file. If the
// backend configuration of the specified name does not exist or is invalid, NewBackend will get failed.
// If the input name is empty, use the current backend. If no current backend is specified or backends
// config is empty, use the default local storage.
// NewBackend creates the Backend with the configuration set in the Kusion configuration file, where the input
// is the configured backend name. If the backend configuration is invalid, NewBackend will get failed. If the
// input name is empty, use the current backend. If no current backend is specified or backends config is empty,
// and the input name is empty, use the default local storage.
func NewBackend(name string) (Backend, error) {
var emptyCfg bool
cfg, err := config.GetConfig()
Expand Down Expand Up @@ -93,3 +95,12 @@ func NewBackend(name string) (Backend, error) {
}
return storage, nil
}

// NewWorkspaceStorage calls NewBackend and WorkspaceStorage to new a workspace storage from specified backend.
func NewWorkspaceStorage(backendName string) (workspace.Storage, error) {
bk, err := NewBackend(backendName)
if err != nil {
return nil, err
}
return bk.WorkspaceStorage()
}
11 changes: 5 additions & 6 deletions pkg/workspace/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ import (

// Storage is used to provide the storage service for multiple workspaces.
type Storage interface {
// Get returns the specified workspace configurations.
// Get returns the workspace configurations. If name is not specified, get the current workspace
// configurations.
Get(name string) (*v1.Workspace, error)

// Create the workspace.
Create(ws *v1.Workspace) error

// Update the workspace.
// Update the workspace. If name is not specified, updates the current workspace, and set the current
// workspace name in the input's name field.
Update(ws *v1.Workspace) error

// Delete deletes the specified workspace.
// Delete deletes the workspace. If name is not specified, deletes the current workspace.
Delete(name string) error

// Exist returns the specified workspace exists or not.
Exist(name string) (bool, error)

// GetNames returns the names of all the existing workspaces.
GetNames() ([]string, error)

Expand Down
111 changes: 44 additions & 67 deletions pkg/workspace/storages/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
type LocalStorage struct {
// The directory path to store the workspace files.
path string

meta *workspacesMetaData
}

// NewLocalStorage news local workspace storage and init default workspace.
Expand All @@ -24,16 +26,19 @@ func NewLocalStorage(path string) (*LocalStorage, error) {
if err := os.MkdirAll(s.path, os.ModePerm); err != nil {
return nil, fmt.Errorf("create workspace directory failed, %w", err)
}
// read workspaces metadata
if err := s.readMeta(); err != nil {
return nil, err
}

return s, s.initDefaultWorkspaceIf()
}

func (s *LocalStorage) Get(name string) (*v1.Workspace, error) {
exist, err := s.Exist(name)
if err != nil {
return nil, err
if name == "" {
name = s.meta.Current
}
if !exist {
if !checkWorkspaceExistence(s.meta, name) {
return nil, ErrWorkspaceNotExist
}

Expand All @@ -51,129 +56,101 @@ func (s *LocalStorage) Get(name string) (*v1.Workspace, error) {
}

func (s *LocalStorage) Create(ws *v1.Workspace) error {
meta, err := s.readMeta()
if err != nil {
return err
}
if checkWorkspaceExistence(meta, ws.Name) {
if checkWorkspaceExistence(s.meta, ws.Name) {
return ErrWorkspaceAlreadyExist
}

if err = s.writeWorkspace(ws); err != nil {
if err := s.writeWorkspace(ws); err != nil {
return err
}

addAvailableWorkspaces(meta, ws.Name)
return s.writeMeta(meta)
addAvailableWorkspaces(s.meta, ws.Name)
return s.writeMeta()
}

func (s *LocalStorage) Update(ws *v1.Workspace) error {
exist, err := s.Exist(ws.Name)
if err != nil {
return err
if ws.Name == "" {
ws.Name = s.meta.Current
}
if !exist {
if !checkWorkspaceExistence(s.meta, ws.Name) {
return ErrWorkspaceNotExist
}

return s.writeWorkspace(ws)
}

func (s *LocalStorage) Delete(name string) error {
meta, err := s.readMeta()
if err != nil {
return err
if name == "" {
name = s.meta.Current
}
if !checkWorkspaceExistence(meta, name) {
if !checkWorkspaceExistence(s.meta, name) {
return nil
}

if err = os.Remove(filepath.Join(s.path, name+yamlSuffix)); err != nil {
if err := os.Remove(filepath.Join(s.path, name+yamlSuffix)); err != nil {
return fmt.Errorf("remove workspace file failed: %w", err)
}

removeAvailableWorkspaces(meta, name)
return s.writeMeta(meta)
}

func (s *LocalStorage) Exist(name string) (bool, error) {
meta, err := s.readMeta()
if err != nil {
return false, err
}
return checkWorkspaceExistence(meta, name), nil
removeAvailableWorkspaces(s.meta, name)
return s.writeMeta()
}

func (s *LocalStorage) GetNames() ([]string, error) {
meta, err := s.readMeta()
if err != nil {
return nil, err
}
return meta.AvailableWorkspaces, nil
return s.meta.AvailableWorkspaces, nil
}

func (s *LocalStorage) GetCurrent() (string, error) {
meta, err := s.readMeta()
if err != nil {
return "", err
}
return meta.Current, nil
return s.meta.Current, nil
}

func (s *LocalStorage) SetCurrent(name string) error {
meta, err := s.readMeta()
if err != nil {
return err
}
if !checkWorkspaceExistence(meta, name) {
if !checkWorkspaceExistence(s.meta, name) {
return ErrWorkspaceNotExist
}
meta.Current = name
return s.writeMeta(meta)
s.meta.Current = name
return s.writeMeta()
}

func (s *LocalStorage) initDefaultWorkspaceIf() error {
meta, err := s.readMeta()
if err != nil {
return err
}
if !checkWorkspaceExistence(meta, defaultWorkspace) {
if !checkWorkspaceExistence(s.meta, DefaultWorkspace) {
// if there is no default workspace, create one with empty workspace.
if err = s.writeWorkspace(&v1.Workspace{Name: defaultWorkspace}); err != nil {
if err := s.writeWorkspace(&v1.Workspace{Name: DefaultWorkspace}); err != nil {
return err
}
addAvailableWorkspaces(meta, defaultWorkspace)
addAvailableWorkspaces(s.meta, DefaultWorkspace)
}

if meta.Current == "" {
meta.Current = defaultWorkspace
if s.meta.Current == "" {
s.meta.Current = DefaultWorkspace
}
return s.writeMeta(meta)
return s.writeMeta()
}

func (s *LocalStorage) readMeta() (*workspacesMetaData, error) {
func (s *LocalStorage) readMeta() error {
content, err := os.ReadFile(filepath.Join(s.path, metadataFile))
if os.IsNotExist(err) {
return &workspacesMetaData{}, nil
s.meta = &workspacesMetaData{}
return nil
} else if err != nil {
return nil, fmt.Errorf("read workspace meta data file failed: %w", err)
return fmt.Errorf("read workspace metadata file failed: %w", err)
}

meta := &workspacesMetaData{}
if err = yaml.Unmarshal(content, meta); err != nil {
return nil, fmt.Errorf("yaml unmarshal workspaces meta data failed: %w", err)
return fmt.Errorf("yaml unmarshal workspaces metadata failed: %w", err)
}
return meta, nil
s.meta = meta
return nil
}

func (s *LocalStorage) writeMeta(meta *workspacesMetaData) error {
content, err := yaml.Marshal(meta)
func (s *LocalStorage) writeMeta() error {
content, err := yaml.Marshal(s.meta)
if err != nil {
return fmt.Errorf("yaml marshal workspaces meta data failed: %w", err)
return fmt.Errorf("yaml marshal workspaces metadata failed: %w", err)
}

if err = os.WriteFile(filepath.Join(s.path, metadataFile), content, os.ModePerm); err != nil {
return fmt.Errorf("write workspaces meta data file failed: %w", err)
return fmt.Errorf("write workspaces metadata file failed: %w", err)
}
return nil
}
Expand Down
49 changes: 3 additions & 46 deletions pkg/workspace/storages/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,7 @@ func TestLocalStorageOperation(t *testing.T) {
s, err := NewLocalStorage(tc.path)
assert.Equal(t, tc.success, err == nil)
if tc.success {
var meta *workspacesMetaData
meta, err = s.readMeta()
assert.NoError(t, err)
assert.Equal(t, tc.expectedMeta, meta)
assert.Equal(t, tc.expectedMeta, s.meta)
}
if tc.deletePath {
_ = os.RemoveAll(tc.path)
Expand Down Expand Up @@ -203,10 +200,7 @@ func TestLocalStorage_Create(t *testing.T) {
err = s.Create(tc.workspace)
assert.Equal(t, tc.success, err == nil)
if tc.success {
var meta *workspacesMetaData
meta, err = s.readMeta()
assert.NoError(t, err)
assert.Equal(t, tc.expectedMeta, meta)
assert.Equal(t, tc.expectedMeta, s.meta)
_ = s.Delete(tc.workspace.Name)
}
})
Expand Down Expand Up @@ -277,50 +271,13 @@ func TestLocalStorage_Delete(t *testing.T) {
err = s.Delete(tc.wsName)
assert.Equal(t, tc.success, err == nil)
if tc.success {
var meta *workspacesMetaData
meta, err = s.readMeta()
assert.NoError(t, err)
assert.Equal(t, tc.expectedMeta, meta)
assert.Equal(t, tc.expectedMeta, s.meta)
_ = s.Create(mockWorkspace(tc.wsName))
}
})
}
}

func TestLocalStorage_Exist(t *testing.T) {
testcases := []struct {
name string
success bool
wsName string
expectedExist bool
}{
{
name: "exist workspace",
success: true,
wsName: "dev",
expectedExist: true,
},
{
name: "not exist workspace",
success: true,
wsName: "pre",
expectedExist: false,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
s, err := NewLocalStorage(testDataFolder("workspaces"))
assert.NoError(t, err)
exist, err := s.Exist(tc.wsName)
assert.Equal(t, tc.success, err == nil)
if tc.success {
assert.Equal(t, tc.expectedExist, exist)
}
})
}
}

func TestLocalStorage_GetNames(t *testing.T) {
testcases := []struct {
name string
Expand Down
Loading
Loading