Skip to content
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
4 changes: 2 additions & 2 deletions pkg/module/generator/ordered_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ var (
"namespace": "foo",
"name": "bar",
},
"Spec": map[string]interface{}{
"spec": map[string]interface{}{
"replica": 1,
"template": map[string]interface{}{
"Spec": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []map[string]interface{}{
{
"image": "foo.bar.com:v1",
Expand Down
11 changes: 0 additions & 11 deletions pkg/module/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,16 @@ import (
"github.com/pkg/errors"
"google.golang.org/grpc"

v1 "kusionstack.io/kusion-api-go/api.kusion.io/v1"
"kusionstack.io/kusion-module-framework/pkg/module/proto"
)

const PluginKey = "module-default"

// Generator is an interface for things that can generate Intent from input configurations.
// todo it's for built-in generators and we should consider to convert it to a general Module interface
type Generator interface {
// Generate performs the intent generate operation.
Generate(intent *v1.Spec) error
}

// Module is the interface that we're exposing as a kusion module plugin.
type Module interface {
Generate(ctx context.Context, req *proto.GeneratorRequest) (*proto.GeneratorResponse, error)
}

// NewGeneratorFunc is a function that returns a Generator.
type NewGeneratorFunc func() (Generator, error)

type GRPCClient struct {
client proto.ModuleClient
}
Expand Down
67 changes: 7 additions & 60 deletions pkg/module/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ func WrapK8sResourceToKusionResource(id string, resource runtime.Object) (*v1.Re
}, nil
}

// KubernetesResourceID returns the ID of a Kubernetes resource based on its type and metadata. Resource ID should be unique in one Spec.
// KubernetesResourceID returns the ID of a Kubernetes resource based on its type and metadata.
// Resource ID usually should be unique in one resource list.
func KubernetesResourceID(typeMeta metav1.TypeMeta, objectMeta metav1.ObjectMeta) string {
// resource id example: apps/v1:Deployment:nginx:nginx-deployment
id := typeMeta.APIVersion + ":" + typeMeta.Kind + ":"
Expand Down Expand Up @@ -99,7 +100,8 @@ type ProviderConfig struct {
ProviderMeta v1.GenericConfig `yaml:"providerMeta" json:"providerMeta"`
}

// TerraformResourceID returns the Kusion resource ID of the Terraform resource. Resource ID should be unique in one Spec.
// TerraformResourceID returns the Kusion resource ID of the Terraform resource.
// Resource ID usually should be unique in one resource list.
func TerraformResourceID(providerCfg ProviderConfig, resType, resName string) (string, error) {
if providerCfg.Version == "" {
return "", ErrEmptyTFProviderVersion
Expand Down Expand Up @@ -168,7 +170,7 @@ func TerraformProviderRegion(providerCfg ProviderConfig) string {
return region.(string)
}

// PatchHealthPolicyToExtension patch the health policy to the `extensions` field of the resource in the Spec.
// PatchHealthPolicyToExtension patch the health policy to the `extensions` field of the Kusion resource.
// Support Kubernetes resource only.
func PatchHealthPolicyToExtension(resource *v1.Resource, healthPolicy string) error {
if resource == nil {
Expand All @@ -192,7 +194,7 @@ func PatchHealthPolicyToExtension(resource *v1.Resource, healthPolicy string) er
return nil
}

// PatchImportResourcesToExtension patch the imported resource to the `extensions` field of the resource in the Spec.
// PatchImportResourcesToExtension patch the imported resource to the `extensions` field of the Kusion resource.
// Support TF resource only.
func PatchImportResourcesToExtension(resource *v1.Resource, importedResource string) error {
if resource == nil {
Expand All @@ -215,7 +217,7 @@ func PatchImportResourcesToExtension(resource *v1.Resource, importedResource str
return nil
}

// PatchKubeConfigPathToExtension patch the kubeConfig path to the `extensions` field of the resource in the Spec.
// PatchKubeConfigPathToExtension patch the kubeConfig path to the `extensions` field of the Kusion resource.
// 1. If $KUBECONFIG environment variable is set, then it is used.
// 2. If not, and the `kubeConfig` in resource extensions is set, then it is used.
// 3. Otherwise, ${HOME}/.kube/config is used.
Expand All @@ -238,35 +240,6 @@ var IgnoreModules = map[string]bool{
"job": true,
}

// CallGeneratorFuncs calls each NewGeneratorFunc in the given slice
// and returns a slice of Generator instances.
func CallGeneratorFuncs(newGenerators ...NewGeneratorFunc) ([]Generator, error) {
gs := make([]Generator, 0, len(newGenerators))
for _, newGenerator := range newGenerators {
if g, err := newGenerator(); err != nil {
return nil, err
} else {
gs = append(gs, g)
}
}
return gs, nil
}

// CallGenerators calls the Generate method of each Generator instance
// returned by the given NewGeneratorFuncs.
func CallGenerators(i *v1.Spec, newGenerators ...NewGeneratorFunc) error {
gs, err := CallGeneratorFuncs(newGenerators...)
if err != nil {
return err
}
for _, g := range gs {
if err := g.Generate(i); err != nil {
return err
}
}
return nil
}

// ForeachOrdered executes the given function on each
// item in the map in order of their keys.
func ForeachOrdered[T any](m map[string]T, f func(key string, value T) error) error {
Expand Down Expand Up @@ -321,32 +294,6 @@ func KusionPathDependency(id, name string) string {
return "$kusion_path." + id + "." + name
}

// AppendToSpec adds a Kubernetes resource to the Spec resources slice.
func AppendToSpec(resourceType v1.Type, resourceID string, i *v1.Spec, resource any) error {
// this function is only used for Kubernetes resources
if resourceType != v1.Kubernetes {
return errors.New("AppendToSpec is only used for Kubernetes resources")
}

gvk := resource.(runtime.Object).GetObjectKind().GroupVersionKind().String()
// fixme: this function converts int to int64 by default
unstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(resource)
if err != nil {
return err
}
r := v1.Resource{
ID: resourceID,
Type: resourceType,
Attributes: unstructured,
DependsOn: nil,
Extensions: map[string]any{
v1.ResourceExtensionGVK: gvk,
},
}
i.Resources = append(i.Resources, r)
return nil
}

// PatchResource patches the resource with the given patch.
func PatchResource[T any](resources map[string][]*v1.Resource, gvk string, patchFunc func(*T) error) error {
var obj T
Expand Down
160 changes: 0 additions & 160 deletions pkg/module/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,6 @@ import (
v1 "kusionstack.io/kusion-api-go/api.kusion.io/v1"
)

type mockGenerator struct {
GenerateFunc func(Spec *v1.Spec) error
}

func (m *mockGenerator) Generate(i *v1.Spec) error {
return m.GenerateFunc(i)
}

func TestCallGenerators(t *testing.T) {
i := &v1.Spec{}

var (
generator1 Generator = &mockGenerator{
GenerateFunc: func(Spec *v1.Spec) error {
return nil
},
}
generator2 Generator = &mockGenerator{
GenerateFunc: func(Spec *v1.Spec) error {
return assert.AnError
},
}
gf1 = func() (Generator, error) { return generator1, nil }
gf2 = func() (Generator, error) { return generator2, nil }
)

err := CallGenerators(i, gf1, gf2)
assert.Error(t, err)
assert.EqualError(t, err, assert.AnError.Error())
}

func TestCallGeneratorFuncs(t *testing.T) {
generatorFunc1 := func() (Generator, error) {
return &mockGenerator{}, nil
}

generatorFunc2 := func() (Generator, error) {
return nil, assert.AnError
}

generators, err := CallGeneratorFuncs(generatorFunc1)
assert.NoError(t, err)
assert.Len(t, generators, 1)
assert.IsType(t, &mockGenerator{}, generators[0])

_, err = CallGeneratorFuncs(generatorFunc2)
assert.ErrorIs(t, err, assert.AnError)
}

func TestForeachOrdered(t *testing.T) {
m := map[string]int{
"b": 2,
Expand Down Expand Up @@ -120,43 +71,6 @@ func TestKubernetesResourceID(t *testing.T) {
assert.Equal(t, "apps/v1:Deployment:example:my-deployment", id)
}

func TestAppendToSpec(t *testing.T) {
i := &v1.Spec{}
resource := &v1.Resource{
ID: "v1:Namespace:fake-project",
Type: "Kubernetes",
Attributes: map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]interface{}{
"creationTimestamp": nil,
"name": "fake-project",
},
"spec": make(map[string]interface{}),
"status": make(map[string]interface{}),
},
}

ns := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "fake-project",
},
}

err := AppendToSpec(v1.Kubernetes, resource.ID, i, ns)

assert.NoError(t, err)
assert.Len(t, i.Resources, 1)
assert.Equal(t, resource.ID, i.Resources[0].ID)
assert.Equal(t, resource.Type, i.Resources[0].Type)
assert.Equal(t, resource.Attributes, i.Resources[0].Attributes)
assert.Equal(t, ns.GroupVersionKind().String(), i.Resources[0].Extensions[v1.ResourceExtensionGVK])
}

func TestUniqueAppName(t *testing.T) {
projectName := "my-project"
stackName := "my-stack"
Expand Down Expand Up @@ -218,77 +132,3 @@ func TestPatchResource(t *testing.T) {
resources["/v1, Kind=Namespace"][0].Attributes["metadata"].(map[string]interface{})["labels"].(map[string]interface{}),
)
}

func TestAddKubeConfigIf(t *testing.T) {
testcases := []struct {
name string
ws *v1.Workspace
i *v1.Spec
expectedSpec *v1.Spec
}{
{
name: "empty workspace runtime config",
ws: &v1.Workspace{Name: "dev"},
i: &v1.Spec{
Resources: v1.Resources{
{
ID: "mock-id-1",
Type: "Kubernetes",
Attributes: map[string]any{
"mock-key": "mock-value",
},
Extensions: nil,
},
},
},
expectedSpec: &v1.Spec{
Resources: v1.Resources{
{
ID: "mock-id-1",
Type: "Kubernetes",
Attributes: map[string]any{
"mock-key": "mock-value",
},
Extensions: nil,
},
},
},
},
{
name: "empty kubeConfig in workspace",
ws: &v1.Workspace{
Name: "dev",
},
i: &v1.Spec{
Resources: v1.Resources{
{
ID: "mock-id-1",
Type: "Kubernetes",
Attributes: map[string]any{
"mock-key": "mock-value",
},
Extensions: nil,
},
},
},
expectedSpec: &v1.Spec{
Resources: v1.Resources{
{
ID: "mock-id-1",
Type: "Kubernetes",
Attributes: map[string]any{
"mock-key": "mock-value",
},
Extensions: nil,
},
},
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, *tc.expectedSpec, *tc.i)
})
}
}