Skip to content

Commit

Permalink
feat: add source graph commnad
Browse files Browse the repository at this point in the history
  • Loading branch information
Yangyang96 committed Sep 24, 2024
1 parent 74acaa7 commit 7dea56c
Show file tree
Hide file tree
Showing 24 changed files with 1,463 additions and 4 deletions.
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)
})
})
}
}
6 changes: 6 additions & 0 deletions pkg/cmd/destroy/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ func (o *DestroyOptions) Run() (err error) {
} else {
rel.Phase = apiv1.ReleasePhaseSucceeded
release.UpdateDestroyRelease(storage, rel)
graphStorage, _ := o.Backend.GraphStorage(o.RefProject.Name, o.RefWorkspace.Name)
// Remove resource graph if resources are destroyed
err := graphStorage.Delete()
if err != nil {
return err
}
}

return err
Expand Down
Loading

0 comments on commit 7dea56c

Please sign in to comment.