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: add function workspaceStorage in interface backend, and implement it in local/mysql/oss/s3 #937

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
6 changes: 4 additions & 2 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import (
"kusionstack.io/kusion/pkg/backend/storages"
"kusionstack.io/kusion/pkg/config"
"kusionstack.io/kusion/pkg/engine/state"
"kusionstack.io/kusion/pkg/workspace"
)

// Backend is used to provide the storage service for Workspace, Spec and State.
type Backend interface {
// todo: add functions to parse storage for workspace, spec and state, the format is like the following:
// WorkspaceStorage() workspace.Storage
// todo: add functions to parse storage for spec, the format is like the following:
// SpecStorage(projectName, stackName string) spec.Storage

WorkspaceStorage() (workspace.Storage, error)

StateStorage(project, stack, workspace string) state.Storage
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/backend/storages/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

// LocalStorage is an implementation of backend.Backend which uses local filesystem as storage.
Expand All @@ -20,3 +22,7 @@ func NewLocalStorage(config *v1.BackendLocalConfig) *LocalStorage {
func (s *LocalStorage) StateStorage(project, stack, workspace string) state.Storage {
return statestorages.NewLocalStorage(statestorages.GenStateFilePath(s.path, project, stack, workspace))
}

func (s *LocalStorage) WorkspaceStorage() (workspace.Storage, error) {
return workspacestorages.NewLocalStorage(workspacestorages.GenWorkspaceDirPath(s.path))
}
34 changes: 32 additions & 2 deletions pkg/backend/storages/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import (
"path/filepath"
"testing"

"github.com/bytedance/mockey"
"github.com/stretchr/testify/assert"

v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

func TestNewLocalStorage(t *testing.T) {
Expand Down Expand Up @@ -40,7 +43,7 @@ func TestLocalStorage_StateStorage(t *testing.T) {
stateStorage state.Storage
}{
{
name: "state storage from s3 backend",
name: "state storage from local backend",
localStorage: &LocalStorage{
path: "kusion",
},
Expand All @@ -56,7 +59,34 @@ func TestLocalStorage_StateStorage(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
stateStorage := tc.localStorage.StateStorage(tc.project, tc.stack, tc.workspace)
assert.Equal(t, stateStorage, tc.stateStorage)
assert.Equal(t, tc.stateStorage, stateStorage)
})
}
}

func TestLocalStorage_WorkspaceStorage(t *testing.T) {
testcases := []struct {
name string
success bool
localStorage *LocalStorage
workspaceStorage workspace.Storage
}{
{
name: "workspace storage from local backend",
success: true,
localStorage: &LocalStorage{
path: "kusion",
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new local workspace storage", t, func() {
mockey.Mock(workspacestorages.NewLocalStorage).Return(&workspacestorages.LocalStorage{}, nil).Build()
_, err := tc.localStorage.WorkspaceStorage()
assert.Equal(t, tc.success, err == nil)
})
})
}
}
6 changes: 6 additions & 0 deletions pkg/backend/storages/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

// MysqlStorage is an implementation of backend.Backend which uses mysql as storage.
Expand Down Expand Up @@ -41,3 +43,7 @@ func NewMysqlStorage(config *v1.BackendMysqlConfig) (*MysqlStorage, error) {
func (s *MysqlStorage) StateStorage(project, stack, workspace string) state.Storage {
return statestorages.NewMysqlStorage(s.db, project, stack, workspace)
}

func (s *MysqlStorage) WorkspaceStorage() (workspace.Storage, error) {
return workspacestorages.NewMysqlStorage(s.db)
}
29 changes: 28 additions & 1 deletion pkg/backend/storages/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

func TestNewMysqlStorage(t *testing.T) {
Expand Down Expand Up @@ -68,7 +69,33 @@ func TestMysqlStorage_StateStorage(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
stateStorage := tc.mysqlStorage.StateStorage(tc.project, tc.stack, tc.workspace)
assert.Equal(t, stateStorage, tc.stateStorage)
assert.Equal(t, tc.stateStorage, stateStorage)
})
}
}

func TestMysqlStorage_WorkspaceStorage(t *testing.T) {
testcases := []struct {
name string
success bool
mysqlStorage *MysqlStorage
}{
{
name: "workspace storage from mysql backend",
success: true,
mysqlStorage: &MysqlStorage{
db: &gorm.DB{},
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new mysql workspace storage", t, func() {
mockey.Mock(workspacestorages.NewMysqlStorage).Return(&workspacestorages.MysqlStorage{}, nil).Build()
_, err := tc.mysqlStorage.WorkspaceStorage()
assert.Equal(t, tc.success, err == nil)
})
})
}
}
6 changes: 6 additions & 0 deletions pkg/backend/storages/oss.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

// OssStorage is an implementation of backend.Backend which uses oss as storage.
Expand All @@ -32,3 +34,7 @@ func NewOssStorage(config *v1.BackendOssConfig) (*OssStorage, error) {
func (s *OssStorage) StateStorage(project, stack, workspace string) state.Storage {
return statestorages.NewOssStorage(s.bucket, statestorages.GenGenericOssStateFileKey(s.prefix, project, stack, workspace))
}

func (s *OssStorage) WorkspaceStorage() (workspace.Storage, error) {
return workspacestorages.NewOssStorage(s.bucket, workspacestorages.GenGenericOssWorkspacePrefixKey(s.prefix))
}
30 changes: 29 additions & 1 deletion pkg/backend/storages/oss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

func TestNewOssStorage(t *testing.T) {
Expand Down Expand Up @@ -69,7 +70,34 @@ func TestOssStorage_StateStorage(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
stateStorage := tc.ossStorage.StateStorage(tc.project, tc.stack, tc.workspace)
assert.Equal(t, stateStorage, tc.stateStorage)
assert.Equal(t, tc.stateStorage, stateStorage)
})
}
}

func TestOssStorage_WorkspaceStorage(t *testing.T) {
testcases := []struct {
name string
success bool
ossStorage *OssStorage
}{
{
name: "workspace storage from oss backend",
success: true,
ossStorage: &OssStorage{
bucket: &oss.Bucket{},
prefix: "kusion",
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new oss workspace storage", t, func() {
mockey.Mock(workspacestorages.NewOssStorage).Return(&workspacestorages.OssStorage{}, nil).Build()
_, err := tc.ossStorage.WorkspaceStorage()
assert.Equal(t, tc.success, err == nil)
})
})
}
}
6 changes: 6 additions & 0 deletions pkg/backend/storages/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

// S3Storage is an implementation of backend.Backend which uses s3 as storage.
Expand Down Expand Up @@ -45,3 +47,7 @@ func NewS3Storage(config *v1.BackendS3Config) (*S3Storage, error) {
func (s *S3Storage) StateStorage(project, stack, workspace string) state.Storage {
return statestorages.NewS3Storage(s.s3, s.bucket, statestorages.GenGenericOssStateFileKey(s.prefix, project, stack, workspace))
}

func (s *S3Storage) WorkspaceStorage() (workspace.Storage, error) {
return workspacestorages.NewS3Storage(s.s3, s.bucket, workspacestorages.GenGenericOssWorkspacePrefixKey(s.prefix))
}
29 changes: 29 additions & 0 deletions pkg/backend/storages/s3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/engine/state"
statestorages "kusionstack.io/kusion/pkg/engine/state/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

func TestNewS3Storage(t *testing.T) {
Expand Down Expand Up @@ -77,3 +78,31 @@ func TestS3Storage_StateStorage(t *testing.T) {
})
}
}

func TestS3Storage_WorkspaceStorage(t *testing.T) {
testcases := []struct {
name string
success bool
s3Storage *S3Storage
}{
{
name: "workspace storage from s3 backend",
success: true,
s3Storage: &S3Storage{
s3: &s3.S3{},
bucket: "infra",
prefix: "kusion",
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new s3 workspace storage", t, func() {
mockey.Mock(workspacestorages.NewS3Storage).Return(&workspacestorages.S3Storage{}, nil).Build()
_, err := tc.s3Storage.WorkspaceStorage()
assert.Equal(t, tc.success, err == nil)
})
})
}
}
22 changes: 20 additions & 2 deletions pkg/workspace/storages/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,38 @@ package storages

import (
"errors"
"fmt"
"path/filepath"
"strings"
)

const (
metadataFile = ".metadata.yml"
workspacesPrefix = "workspaces"
workspaceTable = "workspace"
yamlSuffix = ".yaml"
defaultWorkspace = "default"
metadataFile = ".metadata.yml"
yamlSuffix = ".yaml"
)

var (
ErrWorkspaceNotExist = errors.New("workspace does not exist")
ErrWorkspaceAlreadyExist = errors.New("workspace has already existed")
)

// GenWorkspaceDirPath generates the workspace directory path, which is used for LocalStorage.
func GenWorkspaceDirPath(dir string) string {
return filepath.Join(dir, workspacesPrefix)
}

// GenGenericOssWorkspacePrefixKey generates generic oss workspace prefix key, which is use for OssStorage and S3Storage.
func GenGenericOssWorkspacePrefixKey(prefix string) string {
prefix = strings.TrimPrefix(prefix, "/")
if prefix != "" {
prefix += "/"
}
return fmt.Sprintf("%s%s", prefix, workspacesPrefix)
}

// workspacesMetaData contains the name of current workspace and all workspaces, whose serialization
// result contains in the metadataFile for LocalStorage, OssStorage and S3Storage.
type workspacesMetaData struct {
Expand Down
Loading