Skip to content

Commit

Permalink
feat: update the workspace storage definition and implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
healthjyk committed Mar 19, 2024
1 parent 1aca5e5 commit 1974faf
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 383 deletions.
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

0 comments on commit 1974faf

Please sign in to comment.