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 resource graph command #1296

Merged
merged 1 commit into from
Sep 29, 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
59 changes: 58 additions & 1 deletion pkg/apis/api.kusion.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ const (
FieldHealthPolicy = "healthPolicy"
FieldKCLHealthCheckKCL = "health.kcl"
// kind field in kubernetes resource Attributes
FieldKind = "kind"
FieldKind = "kind"
FieldIsWorkload = "kusion.io/is-workload"
)

// BackendConfigs contains the configuration of multiple backends and the current backend.
Expand Down Expand Up @@ -744,3 +745,59 @@ const (
// The default maximum number of concurrent resource executions for Kusion is 10.
DefaultMaxConcurrent = 10
)

type Status string

// Status is to represent resource status displayed by resource graph after apply succeed
const (
ApplySucceed Status = "Apply succeeded"
ApplyFail Status = "Apply failed"
Reconciled Status = "Apply succeeded | Reconciled"
ReconcileFail Status = "Apply succeeded | Reconcile failed"
)

// Graph represents the structure of a project's resources within a workspace, used by `resource graph` command.
type Graph struct {
// Name of the project
Project string `yaml:"Project" json:"Project"`
// Name of the workspace where the app is deployed
Workspace string `yaml:"Workspace" json:"Workspace"`
// All the resources related to the app
Resources *GraphResources `yaml:"Resources" json:"Resources"`
}

// GraphResources defines the categorized resources related to the application.
type GraphResources struct {
// WorkloadResources contains the resources that are directly related to the workload.
WorkloadResources map[string]*GraphResource `yaml:"WorkloadResources" json:"WorkloadResources"`
// DependencyResources stores resources that are required dependencies for the workload.
DependencyResources map[string]*GraphResource `yaml:"DependencyResources" json:"DependencyResources"`
// OtherResources holds independent resources that are not directly tied to workloads or dependencies.
OtherResources map[string]*GraphResource `yaml:"OtherResources" json:"OtherResources"`
// ResourceIndex is a global mapping of resource IDs to their corresponding resource entries.
ResourceIndex map[string]*ResourceEntry `yaml:"ResourceIndex,omitempty" json:"ResourceIndex,omitempty"`
}

// GraphResource represents an individual resource in the cluster.
type GraphResource struct {
// ID refers to Resource ID.
ID string `yaml:"ID" json:"ID"`
// Type refers to Resource Type in the cluster.
Type string `yaml:"Type" json:"Type"`
// Name refers to Resource name in the cluster.
Name string `yaml:"Name" json:"Name"`
// CloudResourceID refers to Resource ID in the cloud provider.
CloudResourceID string `yaml:"CloudResourceID" json:"CloudResourceID"`
// Resource status after apply.
Status Status `yaml:"Status" json:"Status"`
// Dependents lists the resources that depend on this resource.
Dependents []string `yaml:"Dependents" json:"Dependents"`
// Dependencies lists the resources that this resource relies upon.
Dependencies []string `yaml:"Dependencies" json:"Dependencies"`
}

// ResourceEntry stores a GraphResource and its associated Resource mapping.
type ResourceEntry struct {
Resource *GraphResource
Category map[string]*GraphResource
}
4 changes: 4 additions & 0 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"kusionstack.io/kusion/pkg/backend/storages"
"kusionstack.io/kusion/pkg/config"
"kusionstack.io/kusion/pkg/engine/release"
"kusionstack.io/kusion/pkg/engine/resource/graph"
"kusionstack.io/kusion/pkg/workspace"
)

Expand All @@ -21,6 +22,9 @@ type Backend interface {
// StateStorageWithPath returns the state storage with the specified path.
StateStorageWithPath(path string) (release.Storage, error)

// GraphStorage returns the graph storage.
GraphStorage(project, workspace string) (graph.Storage, error)

// ProjectStorage returns the project directory under release folder.
ProjectStorage() (map[string][]string, error)
}
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/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/engine/release"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
"kusionstack.io/kusion/pkg/engine/resource/graph"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
Expand Down Expand Up @@ -32,6 +34,10 @@ func (s *LocalStorage) StateStorageWithPath(path string) (release.Storage, error
return releasestorages.NewLocalStorage(releasestorages.GenReleasePrefixKeyWithPath(s.path, path))
}

func (s *LocalStorage) GraphStorage(project, workspace string) (graph.Storage, error) {
return graphstorages.NewLocalStorage(graphstorages.GenGraphDirPath(s.path, project, workspace))
}

func (s *LocalStorage) ProjectStorage() (map[string][]string, error) {
return projectstorages.NewLocalStorage(projectstorages.GenProjectDirPath(s.path)).Get()
}
58 changes: 58 additions & 0 deletions pkg/backend/storages/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

Expand Down Expand Up @@ -86,3 +88,59 @@ func TestLocalStorage_ReleaseStorage(t *testing.T) {
})
}
}

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

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new local graph storage", t, func() {
mockey.Mock(graphstorages.NewLocalStorage).Return(&graphstorages.LocalStorage{}, nil).Build()
_, err := tc.localStorage.GraphStorage(tc.project, tc.workspace)
assert.Equal(t, tc.success, err == nil)
})
})
}
}

func TestLocalStorage_ProjectStorage(t *testing.T) {
testcases := []struct {
name string
success bool
localStorage *LocalStorage
}{
{
name: "project 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 project storage", t, func() {
mockey.Mock((*projectstorages.LocalStorage).Get).Return(map[string][]string{}, nil).Build()
_, err := tc.localStorage.ProjectStorage()

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/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/engine/release"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
"kusionstack.io/kusion/pkg/engine/resource/graph"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
Expand Down Expand Up @@ -44,6 +46,10 @@ func (s *OssStorage) StateStorageWithPath(path string) (release.Storage, error)
return releasestorages.NewOssStorage(s.bucket, releasestorages.GenReleasePrefixKeyWithPath(s.prefix, path))
}

func (s *OssStorage) GraphStorage(project, workspace string) (graph.Storage, error) {
return graphstorages.NewOssStorage(s.bucket, graphstorages.GenGenericOssResourcePrefixKey(s.prefix, project, workspace))
}

func (s *OssStorage) ProjectStorage() (map[string][]string, error) {
return projectstorages.NewOssStorage(s.bucket, projectstorages.GenGenericOssReleasePrefixKey(s.prefix)).Get()
}
62 changes: 61 additions & 1 deletion pkg/backend/storages/oss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (

v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

Expand Down Expand Up @@ -78,7 +80,7 @@ func TestOssStorage_ReleaseStorage(t *testing.T) {
project, workspace string
}{
{
name: "release storage from s3 backend",
name: "release storage from oss backend",
success: true,
ossStorage: &OssStorage{
bucket: &oss.Bucket{},
Expand All @@ -99,3 +101,61 @@ func TestOssStorage_ReleaseStorage(t *testing.T) {
})
}
}

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

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new oss graph storage", t, func() {
mockey.Mock(graphstorages.NewOssStorage).Return(&graphstorages.OssStorage{}, nil).Build()
_, err := tc.ossStorage.GraphStorage(tc.project, tc.workspace)
assert.Equal(t, tc.success, err == nil)
})
})
}
}

func TestOssStorage_ProjectStorage(t *testing.T) {
testcases := []struct {
name string
success bool
ossStorage *OssStorage
}{
{
name: "project 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 project storage", t, func() {
mockey.Mock((*projectstorages.OssStorage).Get).Return(map[string][]string{}, nil).Build()
_, err := tc.ossStorage.ProjectStorage()

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/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/engine/release"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
"kusionstack.io/kusion/pkg/engine/resource/graph"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
"kusionstack.io/kusion/pkg/workspace"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
Expand Down Expand Up @@ -57,6 +59,10 @@ func (s *S3Storage) StateStorageWithPath(path string) (release.Storage, error) {
return releasestorages.NewS3Storage(s.s3, s.bucket, releasestorages.GenReleasePrefixKeyWithPath(s.prefix, path))
}

func (s *S3Storage) GraphStorage(project, workspace string) (graph.Storage, error) {
return graphstorages.NewS3Storage(s.s3, s.bucket, graphstorages.GenGenericOssResourcePrefixKey(s.prefix, project, workspace))
}

func (s *S3Storage) ProjectStorage() (map[string][]string, error) {
return projectstorages.NewS3Storage(s.s3, s.bucket, projectstorages.GenGenericOssReleasePrefixKey(s.prefix)).Get()
}
62 changes: 62 additions & 0 deletions pkg/backend/storages/s3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
releasestorages "kusionstack.io/kusion/pkg/engine/release/storages"
graphstorages "kusionstack.io/kusion/pkg/engine/resource/graph/storages"
projectstorages "kusionstack.io/kusion/pkg/project/storages"
workspacestorages "kusionstack.io/kusion/pkg/workspace/storages"
)

Expand Down Expand Up @@ -103,3 +105,63 @@ func TestS3Storage_ReleaseStorage(t *testing.T) {
})
}
}

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

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mockey.PatchConvey("mock new s3 graph storage", t, func() {
mockey.Mock(graphstorages.NewS3Storage).Return(&graphstorages.S3Storage{}, nil).Build()
_, err := tc.s3Storage.GraphStorage(tc.project, tc.workspace)
assert.Equal(t, tc.success, err == nil)
})
})
}
}

func TestS3Storage_ProjectStorage(t *testing.T) {
testcases := []struct {
name string
success bool
s3Storage *S3Storage
}{
{
name: "project 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 project storage", t, func() {
mockey.Mock((*projectstorages.S3Storage).Get).Return(map[string][]string{}, nil).Build()
_, err := tc.s3Storage.ProjectStorage()

assert.Equal(t, tc.success, err == nil)
})
})
}
}
Loading
Loading