From 4c3e96533bf3c4c507393b1d80982fb8d68ccbb6 Mon Sep 17 00:00:00 2001 From: siyul-park Date: Sat, 4 Jan 2025 19:24:27 +0900 Subject: [PATCH] feat: remove chart be simple runtime --- cmd/README.md | 25 -- cmd/README_kr.md | 25 -- cmd/pkg/cli/apply.go | 5 +- cmd/pkg/cli/apply_test.go | 94 -------- cmd/pkg/cli/argument.go | 1 - cmd/pkg/cli/delete.go | 5 +- cmd/pkg/cli/delete_test.go | 46 ---- cmd/pkg/cli/env.go | 1 - cmd/pkg/cli/flag.go | 1 - cmd/pkg/cli/get.go | 5 +- cmd/pkg/cli/get_test.go | 34 --- cmd/pkg/cli/start.go | 10 - cmd/pkg/cli/start_test.go | 53 ----- cmd/pkg/driver/driver.go | 12 +- cmd/pkg/driver/inmemory.go | 14 +- cmd/pkg/driver/inmemory_test.go | 16 +- cmd/pkg/driver/mongo.go | 25 +- cmd/pkg/driver/mongo_test.go | 16 +- cmd/pkg/uniflow/main.go | 24 +- cmd/pkg/uniflowctl/main.go | 15 +- docs/getting_started.md | 23 -- docs/getting_started_kr.md | 25 -- docs/key_concepts.md | 36 --- docs/key_concepts_kr.md | 36 --- driver/mongo/pkg/chart/store.go | 310 ------------------------ driver/mongo/pkg/chart/store_test.go | 197 ---------------- examples/system.yaml | 247 -------------------- pkg/chart/chart.go | 230 ------------------ pkg/chart/chart_test.go | 241 ------------------- pkg/chart/linker.go | 121 ---------- pkg/chart/linker_test.go | 93 -------- pkg/chart/linkhook.go | 34 --- pkg/chart/loader.go | 97 -------- pkg/chart/loader_test.go | 74 ------ pkg/chart/store.go | 13 -- pkg/chart/table.go | 338 --------------------------- pkg/chart/table_test.go | 205 ---------------- pkg/chart/unlinkhook.go | 37 --- pkg/hook/hook.go | 55 ++--- pkg/hook/hook_test.go | 39 +--- pkg/runtime/agent.go | 46 +--- pkg/runtime/agent_test.go | 16 -- pkg/runtime/runtime.go | 100 +------- pkg/runtime/runtime_test.go | 133 ----------- pkg/symbol/loadhook.go | 6 +- pkg/symbol/unloadhook.go | 8 +- 46 files changed, 52 insertions(+), 3135 deletions(-) delete mode 100644 driver/mongo/pkg/chart/store.go delete mode 100644 driver/mongo/pkg/chart/store_test.go delete mode 100644 pkg/chart/chart.go delete mode 100644 pkg/chart/chart_test.go delete mode 100644 pkg/chart/linker.go delete mode 100644 pkg/chart/linker_test.go delete mode 100644 pkg/chart/linkhook.go delete mode 100644 pkg/chart/loader.go delete mode 100644 pkg/chart/loader_test.go delete mode 100644 pkg/chart/store.go delete mode 100644 pkg/chart/table.go delete mode 100644 pkg/chart/table_test.go delete mode 100644 pkg/chart/unlinkhook.go diff --git a/cmd/README.md b/cmd/README.md index cef0555a..54008fdd 100644 --- a/cmd/README.md +++ b/cmd/README.md @@ -10,7 +10,6 @@ Settings can be modified using the `.uniflow.toml` file or system environment va |---------------------|--------------------------|--------------------------| | `database.url` | `DATABASE_URL` | `mem://` or `mongodb://` | | `database.name` | `DATABASE_NAME` | - | -| `collection.charts` | `COLLECTION_CHARTS` | `charts` | | `collection.specs` | `COLLECTION_SPECS` | `specs` | | `collection.values` | `COLLECTION_VALUES` | `values` | @@ -40,12 +39,6 @@ You can specify an initial values file with the `--from-values` flag: ./dist/uniflow start --namespace default --from-values examples/values.yaml ``` -Charts can be initialized using the `--from-charts` flag: - -```sh -./dist/uniflow start --namespace default --from-charts examples/charts.yaml -``` - ## Using Uniflowctl `uniflowctl` is a command used to manage resources within a namespace. @@ -64,12 +57,6 @@ To apply values: ./dist/uniflowctl apply values --namespace default --filename examples/values.yaml ``` -To apply charts: - -```sh -./dist/uniflowctl apply charts --namespace default --filename examples/charts.yaml -``` - ### Delete Command The `delete` command removes all resources defined in the specified file. If no namespace is specified, the `default` namespace is used. @@ -84,12 +71,6 @@ To delete values: ./dist/uniflowctl delete values --namespace default --filename examples/values.yaml ``` -To delete charts: - -```sh -./dist/uniflowctl delete charts --namespace default --filename examples/charts.yaml -``` - ### Get Command The `get` command retrieves all resources in the specified namespace. If no namespace is specified, the `default` namespace is used. @@ -103,9 +84,3 @@ To retrieve values: ```sh ./dist/uniflowctl get values --namespace default ``` - -To retrieve charts: - -```sh -./dist/uniflowctl get charts --namespace default -``` diff --git a/cmd/README_kr.md b/cmd/README_kr.md index 613382bc..47909f45 100644 --- a/cmd/README_kr.md +++ b/cmd/README_kr.md @@ -10,7 +10,6 @@ |---------------------|---------------------|--------------------------| | `database.url` | `DATABASE_URL` | `mem://` 또는 `mongodb://` | | `database.name` | `DATABASE_NAME` | - | -| `collection.charts` | `COLLECTION_CHARTS` | `charts` | | `collection.specs` | `COLLECTION_SPECS` | `specs` | | `collection.values` | `COLLECTION_VALUES` | `values` | @@ -39,12 +38,6 @@ ./dist/uniflow start --namespace default --from-values examples/values.yaml ``` -초기 차트 파일은 `--from-charts` 플래그로 제공할 수 있습니다: - -```sh -./dist/uniflow start --namespace default --from-charts examples/charts.yaml -``` - ## Uniflowctl 사용하기 `uniflowctl`는 네임스페이스 내에서 리소스를 관리하는 명령어입니다. @@ -63,12 +56,6 @@ ./dist/uniflowctl apply values --namespace default --filename examples/values.yaml ``` -차트를 적용하려면: - -```sh -./dist/uniflowctl apply charts --namespace default --filename examples/charts.yaml -``` - ### Delete 명령어 `delete` 명령어는 지정된 파일에 정의된 모든 리소스를 삭제합니다. 네임스페이스를 지정하지 않으면 기본적으로 `default` 네임스페이스가 사용됩니다. @@ -83,12 +70,6 @@ ./dist/uniflowctl delete values --namespace default --filename examples/values.yaml ``` -차트를 삭제하려면: - -```sh -./dist/uniflowctl delete charts --namespace default --filename examples/charts.yaml -``` - ### Get 명령어 `get` 명령어는 지정된 네임스페이스 내 모든 리소스를 조회합니다. 네임스페이스가 지정되지 않으면 기본적으로 `default` 네임스페이스가 사용됩니다. @@ -102,9 +83,3 @@ ```sh ./dist/uniflowctl get values --namespace default ``` - -차트를 조회하려면: - -```sh -./dist/uniflowctl get charts --namespace default -``` diff --git a/cmd/pkg/cli/apply.go b/cmd/pkg/cli/apply.go index f5a1784b..a2fd0180 100644 --- a/cmd/pkg/cli/apply.go +++ b/cmd/pkg/cli/apply.go @@ -2,7 +2,6 @@ package cli import ( "github.com/siyul-park/uniflow/cmd/pkg/resource" - "github.com/siyul-park/uniflow/pkg/chart" resourcebase "github.com/siyul-park/uniflow/pkg/resource" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" @@ -14,7 +13,6 @@ import ( type ApplyConfig struct { SpecStore spec.Store ValueStore value.Store - ChartStore chart.Store FS afero.Fs } @@ -24,11 +22,10 @@ func NewApplyCommand(config ApplyConfig) *cobra.Command { Use: "apply", Short: "Apply resources to the specified namespace", Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - ValidArgs: []string{specs, values, charts}, + ValidArgs: []string{specs, values}, RunE: runs(map[string]func(cmd *cobra.Command) error{ specs: runApplyCommand(config.SpecStore, config.FS), values: runApplyCommand(config.ValueStore, config.FS), - charts: runApplyCommand(config.ChartStore, config.FS), }), } diff --git a/cmd/pkg/cli/apply_test.go b/cmd/pkg/cli/apply_test.go index 7b062796..69adab84 100644 --- a/cmd/pkg/cli/apply_test.go +++ b/cmd/pkg/cli/apply_test.go @@ -8,8 +8,6 @@ import ( "testing" "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" "github.com/spf13/afero" @@ -19,7 +17,6 @@ import ( func TestApplyCommand_Execute(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() fs := afero.NewMemMapFs() @@ -51,7 +48,6 @@ func TestApplyCommand_Execute(t *testing.T) { cmd := NewApplyCommand(ApplyConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) cmd.SetOut(output) @@ -98,7 +94,6 @@ func TestApplyCommand_Execute(t *testing.T) { cmd := NewApplyCommand(ApplyConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) cmd.SetOut(output) @@ -140,7 +135,6 @@ func TestApplyCommand_Execute(t *testing.T) { cmd := NewApplyCommand(ApplyConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) cmd.SetOut(output) @@ -185,7 +179,6 @@ func TestApplyCommand_Execute(t *testing.T) { cmd := NewApplyCommand(ApplyConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) cmd.SetOut(output) @@ -200,91 +193,4 @@ func TestApplyCommand_Execute(t *testing.T) { assert.Len(t, results, 1) assert.Contains(t, output.String(), scrt.Name) }) - - t.Run("InsertChart", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - filename := "charts.json" - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - } - - data, err := json.Marshal(chrt) - assert.NoError(t, err) - - file, err := fs.Create(filename) - assert.NoError(t, err) - defer file.Close() - - _, err = file.Write(data) - assert.NoError(t, err) - - output := new(bytes.Buffer) - - cmd := NewApplyCommand(ApplyConfig{ - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - FS: fs, - }) - cmd.SetOut(output) - cmd.SetErr(output) - cmd.SetArgs([]string{charts, fmt.Sprintf("--%s", flagFilename), filename}) - - err = cmd.Execute() - assert.NoError(t, err) - - results, err := chartStore.Load(ctx, chrt) - assert.NoError(t, err) - assert.Len(t, results, 1) - assert.Contains(t, output.String(), chrt.Name) - }) - - t.Run("UpdateChart", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - filename := "charts.json" - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - } - - _, err := chartStore.Store(ctx, chrt) - assert.NoError(t, err) - - data, err := json.Marshal(chrt) - assert.NoError(t, err) - - file, err := fs.Create(filename) - assert.NoError(t, err) - defer file.Close() - - _, err = file.Write(data) - assert.NoError(t, err) - - output := new(bytes.Buffer) - - cmd := NewApplyCommand(ApplyConfig{ - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - FS: fs, - }) - cmd.SetOut(output) - cmd.SetErr(output) - cmd.SetArgs([]string{charts, fmt.Sprintf("--%s", flagFilename), filename}) - - err = cmd.Execute() - assert.NoError(t, err) - - results, err := chartStore.Load(ctx, chrt) - assert.NoError(t, err) - assert.Len(t, results, 1) - assert.Contains(t, output.String(), chrt.Name) - }) } diff --git a/cmd/pkg/cli/argument.go b/cmd/pkg/cli/argument.go index 3cf896a4..dd09013e 100644 --- a/cmd/pkg/cli/argument.go +++ b/cmd/pkg/cli/argument.go @@ -7,7 +7,6 @@ import ( const ( specs = "specs" values = "values" - charts = "charts" ) func runs(runs map[string]func(cmd *cobra.Command) error) func(cmd *cobra.Command, args []string) error { diff --git a/cmd/pkg/cli/delete.go b/cmd/pkg/cli/delete.go index 35c5c519..85c62bcb 100644 --- a/cmd/pkg/cli/delete.go +++ b/cmd/pkg/cli/delete.go @@ -2,7 +2,6 @@ package cli import ( "github.com/siyul-park/uniflow/cmd/pkg/resource" - "github.com/siyul-park/uniflow/pkg/chart" resourcebase "github.com/siyul-park/uniflow/pkg/resource" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" @@ -14,7 +13,6 @@ import ( type DeleteConfig struct { SpecStore spec.Store ValueStore value.Store - ChartStore chart.Store FS afero.Fs } @@ -24,11 +22,10 @@ func NewDeleteCommand(config DeleteConfig) *cobra.Command { Use: "delete", Short: "Delete resources from the specified namespace", Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - ValidArgs: []string{specs, values, charts}, + ValidArgs: []string{specs, values}, RunE: runs(map[string]func(cmd *cobra.Command) error{ specs: runDeleteCommand(config.SpecStore, config.FS, spec.New), values: runDeleteCommand(config.ValueStore, config.FS, value.New), - charts: runDeleteCommand(config.ChartStore, config.FS, chart.New), }), } diff --git a/cmd/pkg/cli/delete_test.go b/cmd/pkg/cli/delete_test.go index 24bda82d..3a74e001 100644 --- a/cmd/pkg/cli/delete_test.go +++ b/cmd/pkg/cli/delete_test.go @@ -7,8 +7,6 @@ import ( "testing" "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" "github.com/spf13/afero" @@ -18,7 +16,6 @@ import ( func TestDeleteCommand_Execute(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() fs := afero.NewMemMapFs() @@ -51,7 +48,6 @@ func TestDeleteCommand_Execute(t *testing.T) { cmd := NewDeleteCommand(DeleteConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) @@ -92,7 +88,6 @@ func TestDeleteCommand_Execute(t *testing.T) { cmd := NewDeleteCommand(DeleteConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, }) @@ -105,45 +100,4 @@ func TestDeleteCommand_Execute(t *testing.T) { assert.NoError(t, err) assert.Len(t, rValue, 0) }) - - t.Run("DeleteChart", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - filename := "charts.json" - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - } - - data, err := json.Marshal(chrt) - assert.NoError(t, err) - - file, err := fs.Create(filename) - assert.NoError(t, err) - defer file.Close() - - _, err = file.Write(data) - assert.NoError(t, err) - - _, err = chartStore.Store(ctx, chrt) - assert.NoError(t, err) - - cmd := NewDeleteCommand(DeleteConfig{ - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - FS: fs, - }) - - cmd.SetArgs([]string{charts, fmt.Sprintf("--%s", flagFilename), filename}) - - err = cmd.Execute() - assert.NoError(t, err) - - r, err := chartStore.Load(ctx, chrt) - assert.NoError(t, err) - assert.Len(t, r, 0) - }) } diff --git a/cmd/pkg/cli/env.go b/cmd/pkg/cli/env.go index 126dde01..e69105ab 100644 --- a/cmd/pkg/cli/env.go +++ b/cmd/pkg/cli/env.go @@ -5,5 +5,4 @@ const ( EnvDatabaseName = "database.name" EnvCollectionSpecs = "collection.specs" EnvCollectionValues = "collection.values" - EnvCollectionCharts = "collection.charts" ) diff --git a/cmd/pkg/cli/flag.go b/cmd/pkg/cli/flag.go index 5e1fb83a..d85b1c3f 100644 --- a/cmd/pkg/cli/flag.go +++ b/cmd/pkg/cli/flag.go @@ -6,7 +6,6 @@ const ( flagFromSpecs = "from-specs" flagFromValues = "from-values" - flagFromCharts = "from-charts" flagDebug = "debug" flagEnvironment = "env" diff --git a/cmd/pkg/cli/get.go b/cmd/pkg/cli/get.go index eb56e6db..7a55e492 100644 --- a/cmd/pkg/cli/get.go +++ b/cmd/pkg/cli/get.go @@ -2,7 +2,6 @@ package cli import ( "github.com/siyul-park/uniflow/cmd/pkg/resource" - "github.com/siyul-park/uniflow/pkg/chart" resourcebase "github.com/siyul-park/uniflow/pkg/resource" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" @@ -11,7 +10,6 @@ import ( // GetConfig represents the configuration for the get command. type GetConfig struct { - ChartStore chart.Store SpecStore spec.Store ValueStore value.Store } @@ -22,11 +20,10 @@ func NewGetCommand(config GetConfig) *cobra.Command { Use: "get", Short: "Load resources from the specified namespace", Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - ValidArgs: []string{specs, values, charts}, + ValidArgs: []string{specs, values}, RunE: runs(map[string]func(cmd *cobra.Command) error{ specs: runGetCommand(config.SpecStore, spec.New), values: runGetCommand(config.ValueStore, value.New), - charts: runGetCommand(config.ChartStore, chart.New), }), } diff --git a/cmd/pkg/cli/get_test.go b/cmd/pkg/cli/get_test.go index e6e2baad..c010e993 100644 --- a/cmd/pkg/cli/get_test.go +++ b/cmd/pkg/cli/get_test.go @@ -6,8 +6,6 @@ import ( "testing" "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" "github.com/stretchr/testify/assert" @@ -16,7 +14,6 @@ import ( func TestGetCommand_Execute(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() t.Run("GetSpec", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -37,7 +34,6 @@ func TestGetCommand_Execute(t *testing.T) { cmd := NewGetCommand(GetConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -66,7 +62,6 @@ func TestGetCommand_Execute(t *testing.T) { cmd := NewGetCommand(GetConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -77,33 +72,4 @@ func TestGetCommand_Execute(t *testing.T) { assert.Contains(t, output.String(), scrt.Name) }) - - t.Run("GetChart", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - } - - _, err := chartStore.Store(ctx, chrt) - assert.NoError(t, err) - - output := new(bytes.Buffer) - - cmd := NewGetCommand(GetConfig{ - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - }) - cmd.SetOut(output) - cmd.SetErr(output) - cmd.SetArgs([]string{charts}) - - err = cmd.Execute() - assert.NoError(t, err) - - assert.Contains(t, output.String(), chrt.Name) - }) } diff --git a/cmd/pkg/cli/start.go b/cmd/pkg/cli/start.go index 8a46fa1a..b4f62ef1 100644 --- a/cmd/pkg/cli/start.go +++ b/cmd/pkg/cli/start.go @@ -7,7 +7,6 @@ import ( "syscall" tea "github.com/charmbracelet/bubbletea" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/hook" resourcebase "github.com/siyul-park/uniflow/pkg/resource" "github.com/siyul-park/uniflow/pkg/runtime" @@ -24,7 +23,6 @@ type StartConfig struct { Hook *hook.Hook SpecStore spec.Store ValueStore value.Store - ChartStore chart.Store FS afero.Fs } @@ -39,7 +37,6 @@ func NewStartCommand(config StartConfig) *cobra.Command { cmd.PersistentFlags().StringP(flagNamespace, toShorthand(flagNamespace), resourcebase.DefaultNamespace, "Set the namespace for running the workflow") cmd.PersistentFlags().String(flagFromSpecs, "", "Specify the file path containing workflow specifications") cmd.PersistentFlags().String(flagFromValues, "", "Specify the file path containing values for the workflow") - cmd.PersistentFlags().String(flagFromCharts, "", "Specify the file path containing charts for the workflow") cmd.PersistentFlags().Bool(flagDebug, false, "Enable debug mode for detailed output during execution") cmd.PersistentFlags().StringToString(flagEnvironment, nil, "Set environment variables for the workflow execution") @@ -50,7 +47,6 @@ func NewStartCommand(config StartConfig) *cobra.Command { func runStartCommand(config StartConfig) func(cmd *cobra.Command, args []string) error { applySpecs := runApplyCommand(config.SpecStore, config.FS, alias(flagFilename, flagFromSpecs)) applyValues := runApplyCommand(config.ValueStore, config.FS, alias(flagFilename, flagFromValues)) - applyCharts := runApplyCommand(config.ChartStore, config.FS, alias(flagFilename, flagFromCharts)) return func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() @@ -81,9 +77,6 @@ func runStartCommand(config StartConfig) func(cmd *cobra.Command, args []string) if err := applyValues(cmd); err != nil { return err } - if err := applyCharts(cmd); err != nil { - return err - } cmd.SetOut(out) @@ -99,7 +92,6 @@ func runStartCommand(config StartConfig) func(cmd *cobra.Command, args []string) Hook: h, SpecStore: config.SpecStore, ValueStore: config.ValueStore, - ChartStore: config.ChartStore, }) defer r.Close() @@ -111,8 +103,6 @@ func runStartCommand(config StartConfig) func(cmd *cobra.Command, args []string) h.AddLoadHook(a) h.AddUnloadHook(a) - h.AddLinkHook(a) - h.AddUnlinkHook(a) d := NewDebugger( a, diff --git a/cmd/pkg/cli/start_test.go b/cmd/pkg/cli/start_test.go index fa687130..0b7c6212 100644 --- a/cmd/pkg/cli/start_test.go +++ b/cmd/pkg/cli/start_test.go @@ -10,7 +10,6 @@ import ( "github.com/go-faker/faker/v4" "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/hook" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/resource" @@ -28,7 +27,6 @@ func TestStartCommand_Execute(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() fs := afero.NewMemMapFs() @@ -69,7 +67,6 @@ func TestStartCommand_Execute(t *testing.T) { FS: fs, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -114,7 +111,6 @@ func TestStartCommand_Execute(t *testing.T) { FS: fs, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -158,7 +154,6 @@ func TestStartCommand_Execute(t *testing.T) { FS: fs, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -205,7 +200,6 @@ func TestStartCommand_Execute(t *testing.T) { FS: fs, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) cmd.SetOut(output) cmd.SetErr(output) @@ -226,51 +220,4 @@ func TestStartCommand_Execute(t *testing.T) { assert.Fail(t, ctx.Err().Error()) } }) - - t.Run(flagFromCharts, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.TODO(), time.Second) - defer cancel() - - filename := "charts.json" - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - } - - data, _ := json.Marshal(chrt) - - f, _ := fs.Create(filename) - f.Write(data) - - output := new(bytes.Buffer) - - cmd := NewStartCommand(StartConfig{ - Scheme: s, - Hook: h, - FS: fs, - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - }) - cmd.SetOut(output) - cmd.SetErr(output) - cmd.SetContext(ctx) - - cmd.SetArgs([]string{fmt.Sprintf("--%s", flagFromCharts), filename}) - - chartStream, _ := chartStore.Watch(ctx) - defer chartStream.Close() - - go func() { - _ = cmd.Execute() - }() - - select { - case <-chartStream.Next(): - case <-ctx.Done(): - assert.Fail(t, ctx.Err().Error()) - } - }) } diff --git a/cmd/pkg/driver/driver.go b/cmd/pkg/driver/driver.go index 00957121..db8d4b45 100644 --- a/cmd/pkg/driver/driver.go +++ b/cmd/pkg/driver/driver.go @@ -3,21 +3,17 @@ package driver import ( "context" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" ) // Driver defines an interface for interacting with various storage mechanisms. type Driver interface { - // SpecStore returns a store for managing specifications. - SpecStore(ctx context.Context, name string) (spec.Store, error) + // Specs returns a store for managing specifications. + Specs(ctx context.Context, name string) (spec.Store, error) - // ValueStore returns a store for managing values. - ValueStore(ctx context.Context, name string) (value.Store, error) - - // ChartStore returns a store for managing charts. - ChartStore(ctx context.Context, name string) (chart.Store, error) + // Values returns a store for managing values. + Values(ctx context.Context, name string) (value.Store, error) // Close terminates the connection and releases resources. Close(ctx context.Context) error diff --git a/cmd/pkg/driver/inmemory.go b/cmd/pkg/driver/inmemory.go index ec53b4cd..60cef94a 100644 --- a/cmd/pkg/driver/inmemory.go +++ b/cmd/pkg/driver/inmemory.go @@ -3,7 +3,6 @@ package driver import ( "context" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" ) @@ -18,21 +17,16 @@ func NewInMemoryDriver() Driver { return &InMemoryDriver{} } -// SpecStore creates and returns a new in-memory Spec Store. -func (c *InMemoryDriver) SpecStore(_ context.Context, _ string) (spec.Store, error) { +// Specs creates and returns a new in-memory Spec Store. +func (c *InMemoryDriver) Specs(_ context.Context, _ string) (spec.Store, error) { return spec.NewStore(), nil } -// ValueStore creates and returns a new in-memory Value Store. -func (c *InMemoryDriver) ValueStore(_ context.Context, _ string) (value.Store, error) { +// Values creates and returns a new in-memory Value Store. +func (c *InMemoryDriver) Values(_ context.Context, _ string) (value.Store, error) { return value.NewStore(), nil } -// ChartStore creates and returns a new in-memory Chart Store. -func (c *InMemoryDriver) ChartStore(_ context.Context, _ string) (chart.Store, error) { - return chart.NewStore(), nil -} - // Close is a no-op for InMemoryDriver, as there is no actual connection to close. func (c *InMemoryDriver) Close(_ context.Context) error { return nil diff --git a/cmd/pkg/driver/inmemory_test.go b/cmd/pkg/driver/inmemory_test.go index 34a1d227..bc8d4677 100644 --- a/cmd/pkg/driver/inmemory_test.go +++ b/cmd/pkg/driver/inmemory_test.go @@ -24,7 +24,7 @@ func TestInMemoryDriver_SpecStore(t *testing.T) { driver := NewInMemoryDriver() defer driver.Close(ctx) - store, err := driver.SpecStore(ctx, "") + store, err := driver.Specs(ctx, "") assert.NoError(t, err) assert.NotNil(t, store) } @@ -36,19 +36,7 @@ func TestInMemoryDriver_ValueStore(t *testing.T) { driver := NewInMemoryDriver() defer driver.Close(ctx) - store, err := driver.ValueStore(ctx, "") - assert.NoError(t, err) - assert.NotNil(t, store) -} - -func TestInMemoryDriver_ChartStore(t *testing.T) { - ctx, cancel := context.WithTimeout(context.TODO(), time.Second) - defer cancel() - - driver := NewInMemoryDriver() - defer driver.Close(ctx) - - store, err := driver.ChartStore(ctx, "") + store, err := driver.Values(ctx, "") assert.NoError(t, err) assert.NotNil(t, store) } diff --git a/cmd/pkg/driver/mongo.go b/cmd/pkg/driver/mongo.go index b9e2c23f..8e778256 100644 --- a/cmd/pkg/driver/mongo.go +++ b/cmd/pkg/driver/mongo.go @@ -5,11 +5,9 @@ import ( "strings" "github.com/gofrs/uuid" - mongochart "github.com/siyul-park/uniflow/driver/mongo/pkg/chart" mongoserver "github.com/siyul-park/uniflow/driver/mongo/pkg/server" mongospec "github.com/siyul-park/uniflow/driver/mongo/pkg/spec" mongovalue "github.com/siyul-park/uniflow/driver/mongo/pkg/value" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/value" "github.com/tryvium-travels/memongo" @@ -53,23 +51,8 @@ func NewMongoDriver(uri, name string) (Driver, error) { }, nil } -// ChartStore creates and returns a new Chart Store. -func (d *MongoDriver) ChartStore(ctx context.Context, name string) (chart.Store, error) { - if name == "" { - name = "charts" - } - - collection := d.database.Collection(name) - store := mongochart.NewStore(collection) - - if err := store.Index(ctx); err != nil { - return nil, err - } - return store, nil -} - -// SpecStore creates and returns a new Spec Store. -func (d *MongoDriver) SpecStore(ctx context.Context, name string) (spec.Store, error) { +// Specs creates and returns a new Spec Store. +func (d *MongoDriver) Specs(ctx context.Context, name string) (spec.Store, error) { if name == "" { name = "specs" } @@ -83,8 +66,8 @@ func (d *MongoDriver) SpecStore(ctx context.Context, name string) (spec.Store, e return store, nil } -// ValueStore creates and returns a new Value Store. -func (d *MongoDriver) ValueStore(ctx context.Context, name string) (value.Store, error) { +// Values creates and returns a new Value Store. +func (d *MongoDriver) Values(ctx context.Context, name string) (value.Store, error) { if name == "" { name = "values" } diff --git a/cmd/pkg/driver/mongo_test.go b/cmd/pkg/driver/mongo_test.go index ff810acc..06dd84ae 100644 --- a/cmd/pkg/driver/mongo_test.go +++ b/cmd/pkg/driver/mongo_test.go @@ -24,7 +24,7 @@ func TestMongoDriver_SpecStore(t *testing.T) { driver, _ := NewMongoDriver("memongodb://", "") defer driver.Close(ctx) - store, err := driver.SpecStore(ctx, "") + store, err := driver.Specs(ctx, "") assert.NoError(t, err) assert.NotNil(t, store) } @@ -36,19 +36,7 @@ func TestMongoDriver_ValueStore(t *testing.T) { driver, _ := NewMongoDriver("memongodb://", "") defer driver.Close(ctx) - store, err := driver.ValueStore(ctx, "") - assert.NoError(t, err) - assert.NotNil(t, store) -} - -func TestMongoDriver_ChartStore(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - driver, _ := NewMongoDriver("memongodb://", "") - defer driver.Close(ctx) - - store, err := driver.ChartStore(ctx, "") + store, err := driver.Values(ctx, "") assert.NoError(t, err) assert.NotNil(t, store) } diff --git a/cmd/pkg/uniflow/main.go b/cmd/pkg/uniflow/main.go index 68a46e05..bc326d38 100644 --- a/cmd/pkg/uniflow/main.go +++ b/cmd/pkg/uniflow/main.go @@ -34,7 +34,6 @@ const configFile = ".uniflow.toml" const ( topicSpecs = "specs" topicValues = "values" - topicCharts = "charts" opCreateSpecs = "specs.create" opReadSpecs = "specs.read" @@ -45,11 +44,6 @@ const ( opReadValues = "values.read" opUpdateValues = "values.update" opDeleteValues = "values.delete" - - opCreateCharts = "charts.create" - opReadCharts = "charts.read" - opUpdateCharts = "charts.update" - opDeleteCharts = "charts.delete" ) var k = koanf.New(".") @@ -61,9 +55,6 @@ func init() { if err := k.Set(cli.EnvCollectionValues, "values"); err != nil { log.Fatal(err) } - if err := k.Set(cli.EnvCollectionCharts, "charts"); err != nil { - log.Fatal(err) - } _ = k.Load(file.Provider(configFile), toml.Parser()) @@ -81,7 +72,6 @@ func main() { databaseName := k.String(cli.EnvDatabaseName) collectionNodes := k.String(cli.EnvCollectionSpecs) collectionValues := k.String(cli.EnvCollectionValues) - collectionCharts := k.String(cli.EnvCollectionCharts) drv := driver.NewInMemoryDriver() defer drv.Close(ctx) @@ -93,15 +83,11 @@ func main() { } } - specStore, err := drv.SpecStore(ctx, collectionNodes) - if err != nil { - log.Fatal(err) - } - valueStore, err := drv.ValueStore(ctx, collectionValues) + specStore, err := drv.Specs(ctx, collectionNodes) if err != nil { log.Fatal(err) } - chartStore, err := drv.ChartStore(ctx, collectionCharts) + valueStore, err := drv.Values(ctx, collectionValues) if err != nil { log.Fatal(err) } @@ -120,7 +106,6 @@ func main() { signals := map[string]any{ topicSpecs: system.WatchResource(specStore), topicValues: system.WatchResource(valueStore), - topicCharts: system.WatchResource(chartStore), } calls := map[string]any{ opCreateSpecs: system.CreateResource(specStore), @@ -131,10 +116,6 @@ func main() { opReadValues: system.ReadResource(valueStore), opUpdateValues: system.UpdateResource(valueStore), opDeleteValues: system.DeleteResource(valueStore), - opCreateCharts: system.CreateResource(chartStore), - opReadCharts: system.ReadResource(chartStore), - opUpdateCharts: system.UpdateResource(chartStore), - opDeleteCharts: system.DeleteResource(chartStore), } systemAddToScheme := system.AddToScheme() @@ -177,7 +158,6 @@ func main() { cmd.AddCommand(cli.NewStartCommand(cli.StartConfig{ Scheme: scheme, Hook: hook, - ChartStore: chartStore, SpecStore: specStore, ValueStore: valueStore, FS: fs, diff --git a/cmd/pkg/uniflowctl/main.go b/cmd/pkg/uniflowctl/main.go index df91ef28..1a97e469 100644 --- a/cmd/pkg/uniflowctl/main.go +++ b/cmd/pkg/uniflowctl/main.go @@ -27,9 +27,6 @@ func init() { if err := k.Set(cli.EnvCollectionValues, "values"); err != nil { log.Fatal(err) } - if err := k.Set(cli.EnvCollectionCharts, "charts"); err != nil { - log.Fatal(err) - } _ = k.Load(file.Provider(configFile), toml.Parser()) @@ -47,7 +44,6 @@ func main() { databaseName := k.String(cli.EnvDatabaseName) collectionNodes := k.String(cli.EnvCollectionSpecs) collectionValues := k.String(cli.EnvCollectionValues) - collectionCharts := k.String(cli.EnvCollectionCharts) drv := driver.NewInMemoryDriver() defer drv.Close(ctx) @@ -59,15 +55,11 @@ func main() { } } - specStore, err := drv.SpecStore(ctx, collectionNodes) - if err != nil { - log.Fatal(err) - } - valueStore, err := drv.ValueStore(ctx, collectionValues) + specStore, err := drv.Specs(ctx, collectionNodes) if err != nil { log.Fatal(err) } - chartStore, err := drv.ChartStore(ctx, collectionCharts) + valueStore, err := drv.Values(ctx, collectionValues) if err != nil { log.Fatal(err) } @@ -81,19 +73,16 @@ func main() { cmd.AddCommand(cli.NewApplyCommand(cli.ApplyConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, })) cmd.AddCommand(cli.NewDeleteCommand(cli.DeleteConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, FS: fs, })) cmd.AddCommand(cli.NewGetCommand(cli.GetConfig{ SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, })) if err := cmd.Execute(); err != nil { diff --git a/docs/getting_started.md b/docs/getting_started.md index e321a1a1..a035d9a9 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -39,7 +39,6 @@ Settings can be modified using the `.uniflow.toml` file or system environment va |---------------------|--------------------------|--------------------------| | `database.url` | `DATABASE_URL` | `mem://` or `mongodb://` | | `database.name` | `DATABASE_NAME` | - | -| `collection.charts` | `COLLECTION_CHARTS` | `charts` | | `collection.specs` | `COLLECTION_SPECS` | `nodes` | | `collection.values` | `COLLECTION_VALUES` | `values` | @@ -113,12 +112,6 @@ You can specify an initial values file with the `--from-values` flag: ./dist/uniflow start --namespace default --from-values examples/values.yaml ``` -Charts can be initialized using the `--from-charts` flag: - -```sh -./dist/uniflow start --namespace default --from-charts examples/charts.yaml -``` - ## Using Uniflowctl `uniflowctl` is a command used to manage resources within a namespace. @@ -137,12 +130,6 @@ To apply values: ./dist/uniflowctl apply values --namespace default --filename examples/values.yaml ``` -To apply charts: - -```sh -./dist/uniflowctl apply charts --namespace default --filename examples/charts.yaml -``` - ### Delete Command The `delete` command removes all resources defined in the specified file. If no namespace is specified, the `default` namespace is used. @@ -157,11 +144,6 @@ To delete values: ./dist/uniflowctl delete values --namespace default --filename examples/values.yaml ``` -To delete charts: - -```sh -./dist/uniflowctl delete charts --namespace default --filename examples/charts.yaml -``` ### Get Command @@ -177,11 +159,6 @@ To retrieve values: ./dist/uniflowctl get values --namespace default ``` -To retrieve charts: - -```sh -./dist/uniflowctl get charts --namespace default -``` ## Integrating HTTP API diff --git a/docs/getting_started_kr.md b/docs/getting_started_kr.md index 171dd040..47d1c522 100644 --- a/docs/getting_started_kr.md +++ b/docs/getting_started_kr.md @@ -39,7 +39,6 @@ make build |---------------------|---------------------|--------------------------| | `database.url` | `DATABASE_URL` | `mem://` 또는 `mongodb://` | | `database.name` | `DATABASE_NAME` | - | -| `collection.charts` | `COLLECTION_CHARTS` | `charts` | | `collection.specs` | `COLLECTION_SPECS` | `nodes` | | `collection.values` | `COLLECTION_VALUES` | `values` | @@ -113,12 +112,6 @@ pong# ./dist/uniflow start --namespace default --from-values examples/values.yaml ``` -초기 차트 파일은 `--from-charts` 플래그로 제공할 수 있습니다: - -```sh -./dist/uniflow start --namespace default --from-charts examples/charts.yaml -``` - ## Uniflowctl 사용하기 `uniflowctl`는 네임스페이스 내에서 리소스를 관리하는 명령어입니다. @@ -137,12 +130,6 @@ pong# ./dist/uniflowctl apply values --namespace default --filename examples/values.yaml ``` -차트를 적용하려면: - -```sh -./dist/uniflowctl apply charts --namespace default --filename examples/charts.yaml -``` - ### Delete 명령어 `delete` 명령어는 지정된 파일에 정의된 모든 리소스를 삭제합니다. 네임스페이스를 지정하지 않으면 기본적으로 `default` 네임스페이스가 사용됩니다. @@ -157,12 +144,6 @@ pong# ./dist/uniflowctl delete values --namespace default --filename examples/values.yaml ``` -차트를 삭제하려면: - -```sh -./dist/uniflowctl delete charts --namespace default --filename examples/charts.yaml -``` - ### Get 명령어 `get` 명령어는 지정된 네임스페이스 내 모든 리소스를 조회합니다. 네임스페이스가 지정되지 않으면 기본적으로 `default` 네임스페이스가 사용됩니다. @@ -177,12 +158,6 @@ pong# ./dist/uniflowctl get values --namespace default ``` -차트를 조회하려면: - -```sh -./dist/uniflowctl get charts --namespace default -``` - ## HTTP API 통합 HTTP API를 통해 노드 명세를 수정하려면, 관련 워크플로우를 설정해야 합니다. 이를 위해 [기본 확장](../ext/README_kr.md)에 포함된 `syscall` 노드를 사용할 수 있습니다: diff --git a/docs/key_concepts.md b/docs/key_concepts.md index d9370a58..1c09ce1e 100644 --- a/docs/key_concepts.md +++ b/docs/key_concepts.md @@ -57,42 +57,6 @@ data: version. - `data`: Contains the value data structured as key-value pairs. -## Chart - -A chart defines a node that combines multiple nodes to perform more complex operations. Charts are used to set up interactions between nodes. - -```yaml -id: 01908c74-8b22-7cbf-a475-6b6bc871b01b -namespace: default -name: sqlite -annotations: - version: "v1.0.0" -specs: - - kind: sql - name: sql - driver: sqlite3 - source: file::{{ .FILENAME }}:?cache=shared -inbounds: - in: - - name: sql - port: in -outbounds: - out: - - name: sql - port: out -env: - FILENAME: - value: "{{ .filename }}" -``` - -- `id`: A unique identifier in UUID format. UUID V7 is recommended. -- `namespace`: Specifies the namespace to which the chart belongs, defaulting to `default`. -- `name`: Specifies the name of the chart, which must be unique within the same namespace. This name becomes the type of the node specification. -- `annotations`: Additional metadata for the chart, including user-defined key-value pairs such as description and version. -- `specs`: Defines the node specifications that make up the chart. -- `ports`: Defines how the chart's ports connect. It specifies how external ports should connect to internal nodes. -- `env`: Specifies the environment variables required by the chart. If `id` and `name` are empty, this is used as an argument for node specifications that utilize this chart. - ## Node A node is an object that processes data, executing workflows by sending and receiving packets through connected ports. Each node has its own independent processing loop and communicates asynchronously with other nodes. diff --git a/docs/key_concepts_kr.md b/docs/key_concepts_kr.md index f51949f8..939a9656 100644 --- a/docs/key_concepts_kr.md +++ b/docs/key_concepts_kr.md @@ -56,42 +56,6 @@ data: - `annotations`: 변수에 대한 추가 메타데이터입니다. 설명, 버전 등 사용자 정의 키-값 쌍을 포함할 수 있습니다. - `data`: 키-값 쌍으로 구성된 변수 데이터를 포함합니다. -## 차트 - -차트는 여러 개의 노드를 결합하여 더 복잡한 동작을 수행하는 노드를 정의합니다. 차트는 노드들 간의 상호 작용을 설정하는 데 사용됩니다. - -```yaml -id: 01908c74-8b22-7cbf-a475-6b6bc871b01b -namespace: default -name: sqlite -annotations: - version: "v1.0.0" -specs: - - kind: sql - name: sql - driver: sqlite3 - source: file::{{ .FILENAME }}:?cache=shared -inbounds: - in: - - name: sql - port: in -outbounds: - out: - - name: sql - port: out -env: - FILENAME: - value: "{{ .filename }}" -``` - -- `id`: UUID 형식의 고유 식별자입니다. UUID V7을 권장합니다. -- `namespace`: 차트가 속한 네임스페이스를 지정하며, 기본값은 `default`입니다. -- `name`: 차트의 이름을 지정하며, 동일한 네임스페이스 내에서 고유해야 합니다. 이 이름이 노드 명세의 유형이 됩니다. -- `annotations`: 차트에 대한 추가 메타데이터입니다. 설명, 버전 등 사용자 정의 키-값 쌍을 포함할 수 있습니다. -- `specs`: 차트를 구성하는 노드 명세를 정의합니다. -- `ports`: 차트의 연결 방식을 정의합니다. 외부로 노출될 포트가 내부의 어떤 노드와 연결되어야 하는지 정의합니다. -- `env`: 차트에 필요한 환경 변수를 지정합니다. `id`와 `name`이 비어져 있다면 이 차트를 활용하는 노드 명세가 값을 평가하기 위한 인자로 사용됩니다. - ## 노드 노드는 데이터를 처리하는 객체로, 서로 연결된 포트를 통해 패킷을 주고받으며 워크플로우를 실행합니다. 각 노드는 독립적인 처리 루프를 가지며, 비동기적으로 다른 노드와 통신합니다. diff --git a/driver/mongo/pkg/chart/store.go b/driver/mongo/pkg/chart/store.go deleted file mode 100644 index 95939e99..00000000 --- a/driver/mongo/pkg/chart/store.go +++ /dev/null @@ -1,310 +0,0 @@ -package chart - -import ( - "context" - - "github.com/go-playground/validator/v10" - "github.com/gofrs/uuid" - "github.com/pkg/errors" - _ "github.com/siyul-park/uniflow/driver/mongo/pkg/encoding" - "github.com/siyul-park/uniflow/pkg/chart" - "github.com/siyul-park/uniflow/pkg/encoding" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/value" - "go.mongodb.org/mongo-driver/v2/bson" - "go.mongodb.org/mongo-driver/v2/mongo" - "go.mongodb.org/mongo-driver/v2/mongo/options" -) - -// Store manages storage and retrieval of Spec objects in a MongoDB collection. -type Store struct { - collection *mongo.Collection - validate *validator.Validate -} - -// Stream represents a MongoDB change stream for tracking Spec changes. -type Stream struct { - stream *mongo.ChangeStream - ctx context.Context - cancel context.CancelFunc - out chan resource.Event -} - -type changeDocument struct { - OperationType string `bson:"operationType"` - DocumentKey struct { - ID uuid.UUID `bson:"_id"` - } `bson:"documentKey"` -} - -var _ chart.Store = (*Store)(nil) -var _ chart.Stream = (*Stream)(nil) - -// NewStore creates a new Store with the specified MongoDB collection. -func NewStore(collection *mongo.Collection) *Store { - return &Store{ - collection: collection, - validate: validator.New(validator.WithRequiredStructEnabled()), - } -} - -// Index ensures the collection has the required indexes and updates them if necessary. -func (s *Store) Index(ctx context.Context) error { - indexes := []mongo.IndexModel{ - { - Keys: bson.D{ - {Key: chart.KeyNamespace, Value: 1}, - {Key: chart.KeyName, Value: 1}, - }, - Options: options.Index().SetUnique(true). - SetPartialFilterExpression(bson.M{chart.KeyName: bson.M{"$exists": true}}), - }, - } - - _, err := s.collection.Indexes().CreateMany(ctx, indexes) - return err -} - -// Watch returns a Stream that monitors changes matching the specified filter. -func (s *Store) Watch(ctx context.Context, charts ...*chart.Chart) (value.Stream, error) { - filter := s.filter(charts...) - - opts := options.ChangeStream().SetFullDocument(options.UpdateLookup) - changeStream, err := s.collection.Watch(ctx, mongo.Pipeline{bson.D{{Key: "$match", Value: filter}}}, opts) - if err != nil { - return nil, err - } - - stream := newStream(changeStream) - - go func() { - select { - case <-ctx.Done(): - stream.Close() - case <-stream.Done(): - } - }() - - return stream, nil -} - -// Load retrieves Specs from the store that match the given criteria. -func (s *Store) Load(ctx context.Context, charts ...*chart.Chart) ([]*chart.Chart, error) { - filter := s.filter(charts...) - limit := int64(s.limit(charts...)) - - cursor, err := s.collection.Find(ctx, filter, options.Find().SetLimit(limit)) - if err != nil { - return nil, err - } - defer cursor.Close(ctx) - - var result []*chart.Chart - for cursor.Next(ctx) { - chrt := &chart.Chart{} - if err := cursor.Decode(&chrt); err != nil { - return nil, err - } - result = append(result, chrt) - } - - if err := cursor.Err(); err != nil { - return nil, err - } - return result, nil -} - -// Store saves the given Specs into the database. -func (s *Store) Store(ctx context.Context, charts ...*chart.Chart) (int, error) { - var docs []any - for _, chrt := range charts { - if chrt.GetID() == uuid.Nil { - chrt.SetID(uuid.Must(uuid.NewV7())) - } - if chrt.GetNamespace() == "" { - chrt.SetNamespace(resource.DefaultNamespace) - } - - if err := s.validate.Struct(chrt); err != nil { - return 0, errors.WithMessage(encoding.ErrUnsupportedValue, err.Error()) - } - - docs = append(docs, chrt) - } - - res, err := s.collection.InsertMany(ctx, docs) - if err != nil { - if mongo.IsDuplicateKeyError(err) { - return 0, errors.WithMessage(resource.ErrDuplicatedKey, err.Error()) - } - return 0, err - } - return len(res.InsertedIDs), nil -} - -// Swap updates existing Specs in the database with the provided data. -func (s *Store) Swap(ctx context.Context, charts ...*chart.Chart) (int, error) { - ids := make([]uuid.UUID, len(charts)) - for i, chrt := range charts { - if chrt.GetID() == uuid.Nil { - chrt.SetID(uuid.Must(uuid.NewV7())) - } - - if err := s.validate.Struct(chrt); err != nil { - return 0, errors.WithMessage(encoding.ErrUnsupportedValue, err.Error()) - } - - ids[i] = chrt.GetID() - } - - filter := bson.M{"_id": bson.M{"$in": ids}} - - cursor, err := s.collection.Find(ctx, filter) - if err != nil { - return 0, err - } - defer cursor.Close(ctx) - - ok := make(map[uuid.UUID]*chart.Chart) - for cursor.Next(ctx) { - chrt := &chart.Chart{} - if err := cursor.Decode(&chrt); err != nil { - return 0, err - } - ok[chrt.GetID()] = chrt - } - - count := 0 - for _, chrt := range charts { - exist, ok := ok[chrt.GetID()] - if !ok { - continue - } - - chrt.SetNamespace(exist.GetNamespace()) - - filter := bson.M{"_id": chrt.GetID()} - update := bson.M{"$set": chrt} - - res, err := s.collection.UpdateOne(ctx, filter, update) - if err != nil { - return 0, err - } - count += int(res.MatchedCount) - } - return count, nil -} - -// Delete removes Specs from the store based on the provided criteria. -func (s *Store) Delete(ctx context.Context, charts ...*chart.Chart) (int, error) { - filter := s.filter(charts...) - res, err := s.collection.DeleteMany(ctx, filter) - if err != nil { - return 0, err - } - return int(res.DeletedCount), nil -} - -func (s *Store) filter(charts ...*chart.Chart) bson.M { - var orFilters []bson.M - for _, v := range charts { - andFilters := bson.M{} - if v.GetID() != uuid.Nil { - andFilters["_id"] = v.GetID() - } - if v.GetNamespace() != "" { - andFilters[value.KeyNamespace] = v.GetNamespace() - } - if v.GetName() != "" { - andFilters[value.KeyName] = v.GetName() - } - if len(andFilters) > 0 { - orFilters = append(orFilters, andFilters) - } - } - - switch len(orFilters) { - case 0: - return bson.M{} - case 1: - return orFilters[0] - default: - return bson.M{"$or": orFilters} - } -} - -func (s *Store) limit(charts ...*chart.Chart) int { - limit := 0 - for _, v := range charts { - if v.GetID() != uuid.Nil || v.GetName() != "" { - limit += 1 - } else if v.GetNamespace() != "" { - return 0 - } - } - return limit -} - -// newStream creates and returns a new Stream. -func newStream(stream *mongo.ChangeStream) *Stream { - ctx, cancel := context.WithCancel(context.Background()) - - s := &Stream{ - stream: stream, - ctx: ctx, - cancel: cancel, - out: make(chan resource.Event), - } - - go func() { - defer close(s.out) - - for s.stream.Next(s.ctx) { - var doc changeDocument - if err := s.stream.Decode(&doc); err != nil { - continue - } - - var op resource.EventOP - switch doc.OperationType { - case "insert": - op = resource.EventStore - case "update": - op = resource.EventSwap - case "delete": - op = resource.EventDelete - default: - continue - } - - event := resource.Event{ - OP: op, - ID: doc.DocumentKey.ID, - } - - select { - case <-ctx.Done(): - return - case s.out <- event: - } - } - }() - - return s -} - -// Next returns a channel for receiving events from the stream. -func (s *Stream) Next() <-chan resource.Event { - return s.out -} - -// Done returns a channel that is closed when the stream is closed. -func (s *Stream) Done() <-chan struct{} { - return s.ctx.Done() -} - -// Close closes the stream and releases any resources. -func (s *Stream) Close() error { - s.cancel() - return nil -} diff --git a/driver/mongo/pkg/chart/store_test.go b/driver/mongo/pkg/chart/store_test.go deleted file mode 100644 index 946d1dfa..00000000 --- a/driver/mongo/pkg/chart/store_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package chart - -import ( - "context" - "testing" - - "github.com/siyul-park/uniflow/pkg/spec" - - "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/driver/mongo/pkg/server" - "github.com/siyul-park/uniflow/pkg/chart" - "github.com/stretchr/testify/assert" - "go.mongodb.org/mongo-driver/v2/mongo" - "go.mongodb.org/mongo-driver/v2/mongo/options" -) - -func TestStore_Index(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - err := st.Index(ctx) - assert.NoError(t, err) -} - -func TestStore_Watch(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - stream, err := st.Watch(ctx) - assert.NoError(t, err) - assert.NotNil(t, stream) - - defer stream.Close() - - go func() { - for { - if event, ok := <-stream.Next(); ok { - assert.NotZero(t, event.ID) - } else { - return - } - } - }() - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - _, _ = st.Store(ctx, chrt) - _, _ = st.Store(ctx, chrt) - _, _ = st.Delete(ctx, chrt) -} - -func TestStore_Load(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - chrt1 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - chrt2 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - count, err := st.Store(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - loaded, err := st.Load(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Len(t, loaded, 2) -} - -func TestStore_Store(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - chrt1 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: faker.UUIDHyphenated(), - }, - }, - }, - } - chrt2 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - count, err := st.Store(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - loaded, err := st.Load(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Len(t, loaded, 2) -} - -func TestStore_Swap(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - chrt1 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - chrt2 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - count, err := st.Store(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - count, err = st.Swap(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - loaded, err := st.Load(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Len(t, loaded, 2) -} - -func TestMemStore_Delete(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - s := server.New() - defer server.Release(s) - - c, _ := mongo.Connect(options.Client().ApplyURI(s.URI())) - defer c.Disconnect(ctx) - - st := NewStore(c.Database(faker.UUIDHyphenated()).Collection(faker.UUIDHyphenated())) - - chrt1 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - chrt2 := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - count, err := st.Store(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - count, err = st.Delete(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Equal(t, count, 2) - - loaded, err := st.Load(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.Len(t, loaded, 0) -} diff --git a/examples/system.yaml b/examples/system.yaml index a8659c29..b0c360c5 100644 --- a/examples/system.yaml +++ b/examples/system.yaml @@ -58,30 +58,6 @@ - method: DELETE path: /v1/values/:id port: out[13] - - - method: POST - path: /v1/charts - port: out[14] - - method: GET - path: /v1/charts - port: out[15] - - method: PATCH - path: /v1/charts - port: out[16] - - method: DELETE - path: /v1/charts - port: out[17] - - - method: GET - path: /v1/charts/:id - port: out[18] - - method: PATCH - path: /v1/charts/:id - port: out[19] - - method: DELETE - path: /v1/charts/:id - port: out[20] - ports: out[0]: - name: specs_create @@ -129,29 +105,6 @@ - name: values_delete_with_params port: in - out[14]: - - name: charts_create - port: in - out[15]: - - name: charts_read_or_watch - port: in - out[16]: - - name: charts_update - port: in - out[17]: - - name: charts_delete_with_query - port: in - - out[18]: - - name: charts_read_with_params - port: in - out[19]: - - name: charts_update_with_params - port: in - out[20]: - - name: charts_delete_with_params - port: in - - kind: if name: specs_read_or_watch when: '!has(self.header.Connection) || !has(self.header.Upgrade)' @@ -555,206 +508,6 @@ - name: session port: io -- kind: if - name: charts_read_or_watch - when: '!has(self.header.Connection) || !has(self.header.Upgrade)' - ports: - out[0]: - - name: charts_read_with_query - port: in - out[1]: - - name: charts_watch - port: io - -- kind: step - name: charts_create - specs: - - kind: snippet - language: cel - code: 'has(self.body) ? self.body : null' - - kind: syscall - opcode: charts.create - - kind: snippet - language: javascript - code: | - export default function (args) { - return { - body: args, - status: 201 - }; - } - -- kind: step - name: charts_read_with_query - specs: - - kind: snippet - language: javascript - code: | - export default function ({ query = {} } = {}) { - return Object.entries(query).flatMap(([key, values]) => - values?.map((value) => ({ [key]: value })) || [] - ); - } - - kind: syscall - opcode: charts.read - - kind: snippet - language: javascript - code: | - export default function (args) { - return { - body: args, - status: 200 - }; - } - -- kind: step - name: charts_update - specs: - - kind: snippet - language: cel - code: 'has(self.body) ? self.body : null' - - kind: syscall - opcode: charts.update - - kind: snippet - language: javascript - code: | - export default function (args) { - return { - body: args, - status: 200 - }; - } - -- kind: step - name: charts_delete_with_query - specs: - - kind: snippet - language: javascript - code: | - export default function ({ query = {} } = {}) { - return Object.entries(query).flatMap(([key, values]) => - values?.map((value) => ({ [key]: value })) || [] - ); - } - - kind: syscall - opcode: charts.delete - - kind: snippet - language: javascript - code: | - export default function (args) { - return { - status: 204 - }; - } - -- kind: step - name: charts_read_with_params - specs: - - kind: snippet - language: cel - code: 'has(self.params) ? self.params : null' - - kind: syscall - opcode: charts.read - - kind: snippet - language: javascript - code: | - export default function (args) { - if (!args) { - return { - body: "Not Found", - status: 404 - }; - } - return { - body: args.length > 1 ? args : args[0], - status: 200 - }; - } - -- kind: step - name: charts_update_with_params - specs: - - kind: snippet - language: javascript - code: | - export default function ({ body, params }) { - return { ...body, ...params }; - } - - kind: syscall - opcode: charts.update - - kind: snippet - language: javascript - code: | - export default function (args) { - if (!args) { - return { - body: "Not Found", - status: 404 - }; - } - return { - body: args.length > 1 ? args : args[0], - status: 200 - }; - } - -- kind: step - name: charts_delete_with_params - specs: - - kind: snippet - language: javascript - code: | - export default function ({ body, params }) { - return { ...body, ...params }; - } - - kind: syscall - opcode: charts.delete - - kind: snippet - language: javascript - code: | - export default function (args) { - return { - status: 204 - }; - } - -- kind: block - name: charts_watch - specs: - - kind: gateway - name: websocket - protocol: websocket - - - kind: signal - name: signal - topic: charts - ports: - out: - - name: session - port: in - - - kind: session - name: session - ports: - out: - - name: downstream - port: in - - - kind: snippet - name: downstream - language: cel - code: 'self[1]' - ports: - out: - - name: websocket - port: in - inbounds: - io: - - name: websocket - port: io - - name: session - port: io - - kind: switch name: catch matches: diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go deleted file mode 100644 index 9a0b82a6..00000000 --- a/pkg/chart/chart.go +++ /dev/null @@ -1,230 +0,0 @@ -package chart - -import ( - "fmt" - - "github.com/gofrs/uuid" - "github.com/pkg/errors" - "github.com/siyul-park/uniflow/pkg/encoding" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/siyul-park/uniflow/pkg/template" - "github.com/siyul-park/uniflow/pkg/types" - "github.com/siyul-park/uniflow/pkg/value" -) - -// Chart defines the structure that combines multiple nodes into a cluster node. -type Chart struct { - // ID is the unique identifier of the chart. - ID uuid.UUID `json:"id" bson:"_id" yaml:"id" map:"id" validate:"required"` - // Namespace groups charts logically. - Namespace string `json:"namespace" bson:"namespace" yaml:"namespace" map:"namespace" validate:"required"` - // Name is the human-readable name of the chart. - Name string `json:"name,omitempty" bson:"name,omitempty" yaml:"name,omitempty" map:"name,omitempty"` - // Annotations hold additional metadata. - Annotations map[string]string `json:"annotations,omitempty" bson:"annotations,omitempty" yaml:"annotations,omitempty" map:"annotations,omitempty"` - // Specs define the specifications of the chart. - Specs []*spec.Unstructured `json:"specs" bson:"specs" yaml:"specs" map:"specs"` - // Inbounds define the inbound ports of the chart. - Inbounds map[string][]spec.Port `json:"inbounds,omitempty" bson:"inbounds,omitempty" yaml:"inbounds,omitempty" map:"inbounds,omitempty"` - // Outbounds define the outbound ports of the chart. - Outbounds map[string][]spec.Port `json:"outbounds,omitempty" bson:"outbounds,omitempty" yaml:"outbounds,omitempty" map:"outbounds,omitempty"` - // Env holds the environment variables of the chart. - Env map[string][]spec.Value `json:"env,omitempty" bson:"env,omitempty" yaml:"env,omitempty" map:"env,omitempty"` -} - -// Key constants for commonly used fields. -const ( - KeyID = "id" - KeyNamespace = "namespace" - KeyName = "name" - KeyAnnotations = "annotations" - KeySpecs = "specs" - KeyInbounds = "inbounds" - KeyOutbounds = "outbounds" - KeyEnv = "env" -) - -var _ resource.Resource = (*Chart)(nil) - -// New creates and returns a new instance of Chart. -func New() *Chart { - return &Chart{} -} - -// IsBound checks whether any of the values are bound to the chart. -func (c *Chart) IsBound(values ...*value.Value) bool { - for _, vals := range c.Env { - for _, val := range vals { - examples := make([]*value.Value, 0, 2) - if val.ID != uuid.Nil { - examples = append(examples, &value.Value{ID: val.ID}) - } - if val.Name != "" { - examples = append(examples, &value.Value{Namespace: c.GetNamespace(), Name: val.Name}) - } - - for _, scrt := range values { - if len(resource.Match(scrt, examples...)) > 0 { - return true - } - } - } - } - return false -} - -// Bind binds the chart's environment variables to the provided values. -func (c *Chart) Bind(values ...*value.Value) error { - for _, vals := range c.Env { - for i, val := range vals { - if val.IsIdentified() { - example := &value.Value{ - ID: val.ID, - Namespace: c.GetNamespace(), - Name: val.Name, - } - - var scrt *value.Value - for _, s := range values { - if len(resource.Match(s, example)) > 0 { - scrt = s - break - } - } - if scrt == nil { - return errors.WithStack(encoding.ErrUnsupportedValue) - } - - v, err := template.Execute(val.Data, scrt.Data) - if err != nil { - return err - } - - val.ID = scrt.GetID() - val.Name = scrt.GetName() - val.Data = v - - vals[i] = val - } - } - } - return nil -} - -// Build constructs a specs based on the given spec. -func (c *Chart) Build(root spec.Spec) ([]spec.Spec, error) { - doc, err := types.Marshal(root) - if err != nil { - return nil, err - } - - data := types.InterfaceOf(doc) - - env := map[string][]spec.Value{} - for key, vals := range c.Env { - for _, val := range vals { - if !val.IsIdentified() { - v, err := template.Execute(val.Data, data) - if err != nil { - return nil, err - } - val.Data = v - } - env[key] = append(env[key], val) - } - } - - specs := make([]spec.Spec, 0, len(c.Specs)) - for _, sp := range c.Specs { - if sp.GetNamespace() == "" { - sp.SetNamespace(fmt.Sprintf("%s/%s", root.GetNamespace(), root.GetID())) - } - if len(env) > 0 { - sp.SetEnv(env) - } - - specs = append(specs, sp) - } - return specs, nil -} - -// GetID returns the chart's ID. -func (c *Chart) GetID() uuid.UUID { - return c.ID -} - -// SetID sets the chart's ID. -func (c *Chart) SetID(val uuid.UUID) { - c.ID = val -} - -// GetNamespace returns the chart's namespace. -func (c *Chart) GetNamespace() string { - return c.Namespace -} - -// SetNamespace sets the chart's namespace. -func (c *Chart) SetNamespace(val string) { - c.Namespace = val -} - -// GetName returns the chart's name. -func (c *Chart) GetName() string { - return c.Name -} - -// SetName sets the chart's name. -func (c *Chart) SetName(val string) { - c.Name = val -} - -// GetAnnotations returns the chart's annotations. -func (c *Chart) GetAnnotations() map[string]string { - return c.Annotations -} - -// SetAnnotations sets the chart's annotations. -func (c *Chart) SetAnnotations(val map[string]string) { - c.Annotations = val -} - -// GetSpecs returns the chart's specs. -func (c *Chart) GetSpecs() []*spec.Unstructured { - return c.Specs -} - -// SetSpecs sets the chart's specs. -func (c *Chart) SetSpecs(val []*spec.Unstructured) { - c.Specs = val -} - -// GetInbounds returns the chart's inbounds. -func (c *Chart) GetInbounds() map[string][]spec.Port { - return c.Inbounds -} - -// SetInbounds sets the chart's inbounds. -func (c *Chart) SetInbounds(val map[string][]spec.Port) { - c.Inbounds = val -} - -// GetOutbounds returns the chart's outbounds. -func (c *Chart) GetOutbounds() map[string][]spec.Port { - return c.Outbounds -} - -// SetOutbounds sets the chart's outbounds. -func (c *Chart) SetOutbounds(val map[string][]spec.Port) { - c.Outbounds = val -} - -// GetEnv returns the chart's environment data. -func (c *Chart) GetEnv() map[string][]spec.Value { - return c.Env -} - -// SetEnv sets the chart's environment data. -func (c *Chart) SetEnv(val map[string][]spec.Value) { - c.Env = val -} diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go deleted file mode 100644 index 3a21c6e3..00000000 --- a/pkg/chart/chart_test.go +++ /dev/null @@ -1,241 +0,0 @@ -package chart - -import ( - "testing" - - "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/siyul-park/uniflow/pkg/value" - "github.com/stretchr/testify/assert" -) - -func TestChart_IsBound(t *testing.T) { - t.Run("NoValues", func(t *testing.T) { - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - - Env: map[string][]spec.Value{ - "FOO": { - { - ID: uuid.Must(uuid.NewV7()), - Data: "foo", - }, - }, - }, - } - assert.False(t, chrt.IsBound()) - }) - - t.Run("WithValues", func(t *testing.T) { - sec1 := &value.Value{ - ID: uuid.Must(uuid.NewV7()), - } - sec2 := &value.Value{ - ID: uuid.Must(uuid.NewV7()), - } - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - - Env: map[string][]spec.Value{ - "FOO": { - { - ID: sec1.ID, - Data: "foo", - }, - }, - }, - } - assert.True(t, chrt.IsBound(sec1)) - assert.False(t, chrt.IsBound(sec2)) - }) -} - -func TestChart_Bind(t *testing.T) { - t.Run("NoMatchingValue", func(t *testing.T) { - scrt := &value.Value{ - ID: uuid.Must(uuid.NewV7()), - Data: "foo", - } - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Env: map[string][]spec.Value{ - "FOO": { - { - ID: uuid.Must(uuid.NewV7()), - Data: "{{ . }}", - }, - }, - }, - } - err := chrt.Bind(scrt) - assert.Error(t, err) - }) - - t.Run("MatchingValue", func(t *testing.T) { - scrt := &value.Value{ - ID: uuid.Must(uuid.NewV7()), - Data: "foo", - } - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Env: map[string][]spec.Value{ - "FOO": { - { - ID: scrt.ID, - Data: "{{ . }}", - }, - }, - }, - } - err := chrt.Bind(scrt) - assert.NoError(t, err) - assert.Equal(t, "foo", chrt.GetEnv()["FOO"][0].Data) - }) -} - -func TestChart_Build(t *testing.T) { - t.Run("NoEnv", func(t *testing.T) { - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: faker.UUIDHyphenated(), - }, - Fields: map[string]any{ - "foo": "{{ .FOO }}", - }, - }, - }, - } - meta := &spec.Meta{ - Kind: chrt.GetName(), - Namespace: resource.DefaultNamespace, - } - specs, err := chrt.Build(meta) - assert.NoError(t, err) - assert.Len(t, specs, 1) - }) - - t.Run("WithEnv", func(t *testing.T) { - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: faker.UUIDHyphenated(), - }, - Fields: map[string]any{ - "foo": "{{ .FOO }}", - }, - }, - }, - Env: map[string][]spec.Value{ - "FOO": { - { - Data: "foo", - }, - }, - }, - } - meta := &spec.Meta{ - Kind: chrt.GetName(), - Namespace: resource.DefaultNamespace, - } - specs, err := chrt.Build(meta) - assert.NoError(t, err) - assert.Len(t, specs, 1) - }) -} - -func TestChart_ID(t *testing.T) { - chrt := New() - id := uuid.Must(uuid.NewV7()) - chrt.SetID(id) - assert.Equal(t, id, chrt.GetID()) -} - -func TestChart_Namespace(t *testing.T) { - chrt := New() - namespace := "test-namespace" - chrt.SetNamespace(namespace) - assert.Equal(t, namespace, chrt.GetNamespace()) -} - -func TestChart_Name(t *testing.T) { - chrt := New() - name := "test-chart" - chrt.SetName(name) - assert.Equal(t, name, chrt.GetName()) -} - -func TestChart_Annotations(t *testing.T) { - chrt := New() - annotations := map[string]string{"key": "value"} - chrt.SetAnnotations(annotations) - assert.Equal(t, annotations, chrt.GetAnnotations()) -} - -func TestChart_Specs(t *testing.T) { - chrt := New() - specs := []*spec.Unstructured{ - { - Meta: spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: "test", - }, - }, - } - chrt.SetSpecs(specs) - assert.Equal(t, specs, chrt.GetSpecs()) -} - -func TestChart_Inbounds(t *testing.T) { - chrt := New() - ports := map[string][]spec.Port{ - "http": { - { - ID: uuid.Must(uuid.NewV7()), - Name: "http", - Port: "80", - }, - }, - } - chrt.SetInbounds(ports) - assert.Equal(t, ports, chrt.GetInbounds()) -} - -func TestChart_Outbounds(t *testing.T) { - chrt := New() - ports := map[string][]spec.Port{ - "http": { - { - ID: uuid.Must(uuid.NewV7()), - Name: "http", - Port: "80", - }, - }, - } - chrt.SetOutbounds(ports) - assert.Equal(t, ports, chrt.GetOutbounds()) -} - -func TestChart_Env(t *testing.T) { - chrt := New() - env := map[string][]spec.Value{ - "FOO": { - { - ID: uuid.Must(uuid.NewV7()), - Data: "bar", - }, - }, - } - chrt.SetEnv(env) - assert.Equal(t, env, chrt.GetEnv()) -} diff --git a/pkg/chart/linker.go b/pkg/chart/linker.go deleted file mode 100644 index 2a3b891d..00000000 --- a/pkg/chart/linker.go +++ /dev/null @@ -1,121 +0,0 @@ -package chart - -import ( - "sync" - - "github.com/siyul-park/uniflow/pkg/node" - "github.com/siyul-park/uniflow/pkg/scheme" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/siyul-park/uniflow/pkg/symbol" -) - -// Linker manages chart loading and unloading. -type Linker struct { - scheme *scheme.Scheme - codecs map[string]scheme.Codec - mu sync.RWMutex -} - -var _ LinkHook = (*Linker)(nil) -var _ UnlinkHook = (*Linker)(nil) - -// NewLinker creates a new Linker. -func NewLinker(s *scheme.Scheme) *Linker { - return &Linker{ - scheme: s, - codecs: make(map[string]scheme.Codec), - } -} - -// Link loads the chart, creating nodes and symbols. -func (l *Linker) Link(chrt *Chart) error { - l.mu.Lock() - defer l.mu.Unlock() - - kind := chrt.GetName() - codec := l.codecs[kind] - - if l.scheme.Codec(kind) != codec { - return nil - } - - codec = scheme.CodecFunc(func(root spec.Spec) (node.Node, error) { - specs, err := chrt.Build(root) - if err != nil { - return nil, err - } - - symbols := make([]*symbol.Symbol, 0, len(specs)) - for _, sp := range specs { - unstructured := &spec.Unstructured{} - if err := spec.As(sp, unstructured); err != nil { - for _, sb := range symbols { - _ = sb.Close() - } - return nil, err - } else if err := unstructured.Build(); err != nil { - for _, sb := range symbols { - _ = sb.Close() - } - return nil, err - } else if decode, err := l.scheme.Decode(unstructured); err != nil { - for _, sb := range symbols { - _ = sb.Close() - } - return nil, err - } else { - sp = decode - } - - n, err := l.scheme.Compile(sp) - if err != nil { - for _, sb := range symbols { - _ = sb.Close() - } - return nil, err - } - - symbols = append(symbols, &symbol.Symbol{ - Spec: sp, - Node: n, - }) - } - - n := symbol.NewCluster(symbols) - - for name, ports := range chrt.GetInbounds() { - for _, port := range ports { - n.Inbound(name, port) - } - } - - for name, ports := range chrt.GetOutbounds() { - for _, port := range ports { - n.Outbound(name, port) - } - } - - return n, nil - }) - - l.scheme.AddCodec(kind, codec) - l.codecs[kind] = codec - return nil -} - -// Unlink removes the chart from the scheme. -func (l *Linker) Unlink(chrt *Chart) error { - l.mu.Lock() - defer l.mu.Unlock() - - kind := chrt.GetName() - codec := l.codecs[kind] - - if l.scheme.Codec(kind) != codec { - return nil - } - - l.scheme.RemoveCodec(kind) - delete(l.codecs, kind) - return nil -} diff --git a/pkg/chart/linker_test.go b/pkg/chart/linker_test.go deleted file mode 100644 index c30f3881..00000000 --- a/pkg/chart/linker_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package chart - -import ( - "testing" - - "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/node" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/scheme" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/siyul-park/uniflow/pkg/value" - "github.com/stretchr/testify/assert" -) - -func TestLinker_Link(t *testing.T) { - s := scheme.New() - kind := faker.UUIDHyphenated() - - s.AddKnownType(kind, &spec.Meta{}) - s.AddCodec(kind, scheme.CodecFunc(func(spec spec.Spec) (node.Node, error) { - return node.NewOneToOneNode(nil), nil - })) - - l := NewLinker(s) - - scrt := &value.Value{ID: uuid.Must(uuid.NewV7())} - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: kind, - Name: "dummy", - }, - }, - }, - Env: map[string][]spec.Value{ - "key1": { - { - ID: scrt.GetID(), - Data: faker.UUIDHyphenated(), - }, - }, - "key2": { - { - Data: "{{ .id }}", - }, - }, - }, - Inbounds: map[string][]spec.Port{ - node.PortIn: { - { - Name: "dummy", - Port: node.PortIn, - }, - }, - }, - } - - meta := &spec.Meta{ - Kind: chrt.GetName(), - Namespace: resource.DefaultNamespace, - } - - err := l.Link(chrt) - assert.NoError(t, err) - assert.Contains(t, s.Kinds(), chrt.GetName()) - - n, err := s.Compile(meta) - assert.NoError(t, err) - assert.NotNil(t, n) -} - -func TestLinker_Unlink(t *testing.T) { - s := scheme.New() - - l := NewLinker(s) - - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - } - - l.Link(chrt) - - err := l.Unlink(chrt) - assert.NoError(t, err) - assert.NotContains(t, s.Kinds(), chrt.GetName()) -} diff --git a/pkg/chart/linkhook.go b/pkg/chart/linkhook.go deleted file mode 100644 index b62c282d..00000000 --- a/pkg/chart/linkhook.go +++ /dev/null @@ -1,34 +0,0 @@ -package chart - -// LinkHook defines an interface for handling the loading of a chart. -type LinkHook interface { - // Link processes the loading of a chart and may return an error. - Link(*Chart) error -} - -type LinkHooks []LinkHook - -type linkHook struct { - link func(*Chart) error -} - -var _ LinkHook = (LinkHooks)(nil) -var _ LinkHook = (*linkHook)(nil) - -// LinkFunc creates a LoadHook from the given function. -func LinkFunc(link func(*Chart) error) LinkHook { - return &linkHook{link: link} -} - -func (h LinkHooks) Link(chrt *Chart) error { - for _, hook := range h { - if err := hook.Link(chrt); err != nil { - return err - } - } - return nil -} - -func (h *linkHook) Link(chrt *Chart) error { - return h.link(chrt) -} diff --git a/pkg/chart/loader.go b/pkg/chart/loader.go deleted file mode 100644 index 4585f017..00000000 --- a/pkg/chart/loader.go +++ /dev/null @@ -1,97 +0,0 @@ -package chart - -import ( - "context" - "errors" - - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/value" -) - -// LoaderConfig holds configuration for the Loader. -type LoaderConfig struct { - Table *Table // Lookup table for storing loaded symbols - ChartStore Store // ChartStore to retrieve charts from - ValueStore value.Store // ValueStore to retrieve values from -} - -// Loader synchronizes with spec.Store to load spec.Spec into the Table. -type Loader struct { - table *Table - chartStore Store - valueStore value.Store -} - -// NewLoader creates a new Loader instance with the provided configuration. -func NewLoader(config LoaderConfig) *Loader { - return &Loader{ - table: config.Table, - chartStore: config.ChartStore, - valueStore: config.ValueStore, - } -} - -// Load loads charts and binds them with values, then inserts them into the table. -func (l *Loader) Load(ctx context.Context, charts ...*Chart) error { - examples := charts - - charts, err := l.chartStore.Load(ctx, examples...) - if err != nil { - return err - } - - var values []*value.Value - for _, chrt := range charts { - for _, vals := range chrt.GetEnv() { - for _, val := range vals { - if val.ID == uuid.Nil && val.Name == "" { - continue - } - values = append(values, &value.Value{ - ID: val.ID, - Namespace: chrt.GetNamespace(), - Name: val.Name, - }) - } - } - } - - if len(values) > 0 { - if values, err = l.valueStore.Load(ctx, values...); err != nil { - return err - } - } - - var errs []error - loaded := make([]*Chart, 0, len(charts)) - for _, chrt := range charts { - if err := chrt.Bind(values...); err != nil { - errs = append(errs, err) - } else if err := l.table.Insert(chrt); err != nil { - errs = append(errs, err) - } else { - loaded = append(loaded, chrt) - } - } - - for _, id := range l.table.Keys() { - chrt := l.table.Lookup(id) - if chrt != nil && len(resource.Match(chrt, examples...)) > 0 { - ok := false - for _, c := range loaded { - if c.GetID() == id { - ok = true - break - } - } - if !ok { - if _, err := l.table.Free(id); err != nil { - errs = append(errs, err) - } - } - } - } - - return errors.Join(errs...) -} diff --git a/pkg/chart/loader_test.go b/pkg/chart/loader_test.go deleted file mode 100644 index 17b5c10b..00000000 --- a/pkg/chart/loader_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package chart - -import ( - "context" - "testing" - - "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/siyul-park/uniflow/pkg/value" - "github.com/stretchr/testify/assert" -) - -func TestLoader_Load(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - chartStore := NewStore() - valueStore := value.NewStore() - - table := NewTable() - defer table.Close() - - loader := NewLoader(LoaderConfig{ - Table: table, - ChartStore: chartStore, - ValueStore: valueStore, - }) - - scrt := &value.Value{ - ID: uuid.Must(uuid.NewV7()), - Data: faker.UUIDHyphenated(), - } - - chrt1 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - - Env: map[string][]spec.Value{ - "key": { - { - ID: scrt.GetID(), - Data: faker.UUIDHyphenated(), - }, - }, - }, - } - chrt2 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: chrt1.GetName(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - valueStore.Store(ctx, scrt) - - chartStore.Store(ctx, chrt1) - chartStore.Store(ctx, chrt2) - - err := loader.Load(ctx, chrt1, chrt2) - assert.NoError(t, err) - assert.NotNil(t, table.Lookup(chrt1.GetID())) - assert.NotNil(t, table.Lookup(chrt2.GetID())) -} diff --git a/pkg/chart/store.go b/pkg/chart/store.go deleted file mode 100644 index b71bfdc3..00000000 --- a/pkg/chart/store.go +++ /dev/null @@ -1,13 +0,0 @@ -package chart - -import "github.com/siyul-park/uniflow/pkg/resource" - -// Store is an alias for the resource.Store interface, specialized for Chart resources. -type Store resource.Store[*Chart] - -type Stream = resource.Stream - -// NewStore creates and returns a new instance of a Store for managing Chart resources. -func NewStore() Store { - return resource.NewStore[*Chart]() -} diff --git a/pkg/chart/table.go b/pkg/chart/table.go deleted file mode 100644 index e3a09f4a..00000000 --- a/pkg/chart/table.go +++ /dev/null @@ -1,338 +0,0 @@ -package chart - -import ( - "slices" - "sync" - - "github.com/gofrs/uuid" -) - -// TableOption holds configurations for a Table instance. -type TableOption struct { - LinkHooks []LinkHook // LoadHooks are functions executed when symbols are loaded. - UnlinkHooks []UnlinkHook // UnloadHooks are functions executed when symbols are unloaded. -} - -// Table manages charts and their references, allowing insertion, lookup, and removal. -type Table struct { - charts map[uuid.UUID]*Chart - namespaces map[string]map[string]uuid.UUID - references map[uuid.UUID][]uuid.UUID - linkHooks LinkHooks - unlinkHooks UnlinkHooks - mu sync.RWMutex -} - -// NewTable creates and returns a new Table instance with the provided options. -func NewTable(opts ...TableOption) *Table { - var linkHooks []LinkHook - var unlinkHooks []UnlinkHook - for _, opt := range opts { - linkHooks = append(linkHooks, opt.LinkHooks...) - unlinkHooks = append(unlinkHooks, opt.UnlinkHooks...) - } - - return &Table{ - charts: make(map[uuid.UUID]*Chart), - namespaces: make(map[string]map[string]uuid.UUID), - references: make(map[uuid.UUID][]uuid.UUID), - linkHooks: linkHooks, - unlinkHooks: unlinkHooks, - } -} - -// Insert adds a new chart to the table, freeing the previous chart if it exists. -func (t *Table) Insert(chrt *Chart) error { - t.mu.Lock() - defer t.mu.Unlock() - - if _, err := t.free(chrt.GetID()); err != nil { - return err - } - return t.insert(chrt) -} - -// Free removes a chart from the table based on its UUID and unloads it. -func (t *Table) Free(id uuid.UUID) (bool, error) { - t.mu.Lock() - defer t.mu.Unlock() - - chrt, err := t.free(id) - if err != nil { - return false, err - } - return chrt != nil, nil -} - -// Lookup retrieves a chart from the table based on its UUID. -func (t *Table) Lookup(id uuid.UUID) *Chart { - t.mu.RLock() - defer t.mu.RUnlock() - - return t.charts[id] -} - -// Links returns the charts linked to the chart specified by its UUID. -func (t *Table) Links(id uuid.UUID) []*Chart { - t.mu.RLock() - defer t.mu.RUnlock() - - chrt, ok := t.charts[id] - if !ok { - return nil - } - return t.linked(chrt) -} - -// Keys returns all IDs of charts in the table. -func (t *Table) Keys() []uuid.UUID { - t.mu.RLock() - defer t.mu.RUnlock() - - var ids []uuid.UUID - for id := range t.charts { - ids = append(ids, id) - } - return ids -} - -// Close removes all charts from the table and unloads them. -func (t *Table) Close() error { - t.mu.Lock() - defer t.mu.Unlock() - - degree := map[*Chart]int{} - for id, chrt := range t.charts { - degree[chrt] = 0 - for _, ports := range t.references[id] { - degree[chrt] += len(ports) - } - } - - var queue []*Chart - for chrt, count := range degree { - if count == 0 { - queue = append(queue, chrt) - } - } - - charts := make([]*Chart, 0, len(t.charts)) - for len(queue) > 0 { - curr := queue[0] - queue = queue[1:] - - if slices.Contains(charts, curr) { - continue - } - charts = append(charts, curr) - - for _, spec := range curr.GetSpecs() { - id := t.lookup(curr.GetNamespace(), spec.GetKind()) - if next, ok := t.charts[id]; ok { - degree[next]-- - if degree[next] == 0 { - queue = append(queue, next) - } - } - } - } - for chrt, count := range degree { - if count != 0 { - charts = append(charts, chrt) - } - } - - for _, chrt := range charts { - if _, err := t.free(chrt.GetID()); err != nil { - return err - } - } - return nil -} - -func (t *Table) insert(chrt *Chart) error { - t.charts[chrt.GetID()] = chrt - - ns, ok := t.namespaces[chrt.GetNamespace()] - if !ok { - ns = make(map[string]uuid.UUID) - t.namespaces[chrt.GetNamespace()] = ns - } - ns[chrt.GetName()] = chrt.GetID() - - t.links(chrt) - return t.load(chrt) -} - -func (t *Table) free(id uuid.UUID) (*Chart, error) { - chrt, ok := t.charts[id] - if !ok { - return nil, nil - } - - if err := t.unload(chrt); err != nil { - return nil, err - } - t.unlinks(chrt) - - if ns, ok := t.namespaces[chrt.GetNamespace()]; ok { - delete(ns, chrt.GetName()) - if len(ns) == 0 { - delete(t.namespaces, chrt.GetNamespace()) - } - } - - delete(t.charts, id) - return chrt, nil -} - -func (t *Table) load(chrt *Chart) error { - linked := t.linked(chrt) - for _, chrt := range linked { - if t.active(chrt) { - if err := t.linkHooks.Link(chrt); err != nil { - return err - } - } - } - return nil -} - -func (t *Table) unload(chrt *Chart) error { - linked := t.linked(chrt) - for i := len(linked) - 1; i >= 0; i-- { - chrt := linked[i] - if t.active(chrt) { - if err := t.unlinkHooks.Unlink(chrt); err != nil { - return err - } - } - } - return nil -} - -func (t *Table) links(chrt *Chart) { - for _, spec := range chrt.GetSpecs() { - id := t.lookup(chrt.GetNamespace(), spec.GetKind()) - if id != uuid.Nil && !slices.Contains(t.references[id], chrt.GetID()) { - t.references[id] = append(t.references[id], chrt.GetID()) - } - } - - for _, ref := range t.charts { - for _, spec := range ref.GetSpecs() { - id := t.lookup(ref.GetNamespace(), spec.GetKind()) - if id != uuid.Nil && id == chrt.GetID() { - if !slices.Contains(t.references[id], ref.GetID()) { - t.references[id] = append(t.references[id], ref.GetID()) - } - } - } - } -} - -func (t *Table) unlinks(chrt *Chart) { - for _, spec := range chrt.GetSpecs() { - id := t.lookup(chrt.GetNamespace(), spec.GetKind()) - - references := t.references[id] - for i := 0; i < len(references); i++ { - if references[i] == chrt.GetID() { - references = append(references[:i], references[i+1:]...) - i-- - } - } - - if len(references) > 0 { - t.references[id] = references - } else { - delete(t.references, id) - } - } - delete(t.references, chrt.GetID()) -} - -func (t *Table) linked(chrt *Chart) []*Chart { - degree := map[*Chart]int{} - visited := map[*Chart]struct{}{} - queue := []*Chart{chrt} - for len(queue) > 0 { - curr := queue[0] - queue = queue[1:] - - if _, ok := visited[curr]; ok { - continue - } - visited[curr] = struct{}{} - - for _, id := range t.references[curr.GetID()] { - if next, ok := t.charts[id]; ok { - degree[next]++ - queue = append(queue, next) - } - } - } - - var linked []*Chart - queue = []*Chart{chrt} - for len(queue) > 0 { - curr := queue[0] - queue = queue[1:] - - if slices.Contains(linked, curr) { - continue - } - linked = append(linked, curr) - - for _, id := range t.references[curr.GetID()] { - if next, ok := t.charts[id]; ok { - degree[next]-- - if degree[next] == 0 { - queue = append(queue, next) - } - } - } - } - for curr, count := range degree { - if count != 0 { - linked = append(linked, curr) - } - } - - return linked -} - -func (t *Table) active(chrt *Chart) bool { - var linked []*Chart - stack := []*Chart{chrt} - for len(stack) > 0 { - curr := stack[len(stack)-1] - ok := true - for _, sp := range curr.Specs { - id := t.lookup(curr.GetNamespace(), sp.GetKind()) - next := t.charts[id] - - if next == nil || slices.Contains(stack, next) { - return false - } - - ok = slices.Contains(linked, next) - if !ok { - stack = append(stack, next) - break - } - } - if ok { - stack = stack[0 : len(stack)-1] - linked = append(linked, curr) - } - } - return true -} - -func (t *Table) lookup(namespace, name string) uuid.UUID { - if ns, ok := t.namespaces[namespace]; ok { - return ns[name] - } - return uuid.Nil -} diff --git a/pkg/chart/table_test.go b/pkg/chart/table_test.go deleted file mode 100644 index c08d0349..00000000 --- a/pkg/chart/table_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package chart - -import ( - "testing" - - "github.com/go-faker/faker/v4" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/resource" - "github.com/siyul-park/uniflow/pkg/spec" - "github.com/stretchr/testify/assert" -) - -func TestTable_Insert(t *testing.T) { - tb := NewTable() - defer tb.Close() - - chrt1 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - } - chrt2 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: chrt1.GetName(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - err := tb.Insert(chrt1) - assert.NoError(t, err) - assert.NotNil(t, tb.Lookup(chrt1.GetID())) - - err = tb.Insert(chrt2) - assert.NoError(t, err) - assert.NotNil(t, tb.Lookup(chrt2.GetID())) -} - -func TestTable_Free(t *testing.T) { - tb := NewTable() - defer tb.Close() - - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: faker.UUIDHyphenated(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - err := tb.Insert(chrt) - assert.NoError(t, err) - - ok, err := tb.Free(chrt.GetID()) - assert.NoError(t, err) - assert.True(t, ok) -} - -func TestTable_Lookup(t *testing.T) { - tb := NewTable() - defer tb.Close() - - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: faker.UUIDHyphenated(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - err := tb.Insert(chrt) - assert.NoError(t, err) - assert.Equal(t, chrt, tb.Lookup(chrt.GetID())) -} - -func TestTable_Links(t *testing.T) { - tb := NewTable() - defer tb.Close() - - chrt1 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - } - chrt2 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: chrt1.GetName(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - tb.Insert(chrt1) - tb.Insert(chrt2) - - links := tb.Links(chrt1.GetID()) - assert.Equal(t, []*Chart{chrt1, chrt2}, links) - - links = tb.Links(chrt2.GetID()) - assert.Equal(t, []*Chart{chrt2}, links) -} - -func TestTable_Keys(t *testing.T) { - tb := NewTable() - defer tb.Close() - - chrt := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: faker.UUIDHyphenated(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - tb.Insert(chrt) - - ids := tb.Keys() - assert.Contains(t, ids, chrt.GetID()) -} - -func TestTable_Hook(t *testing.T) { - loaded := 0 - unloaded := 0 - - tb := NewTable(TableOption{ - LinkHooks: []LinkHook{ - LinkFunc(func(_ *Chart) error { - loaded += 1 - return nil - }), - }, - UnlinkHooks: []UnlinkHook{ - UnlinkFunc(func(_ *Chart) error { - unloaded += 1 - return nil - }), - }, - }) - defer tb.Close() - - chrt1 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - } - chrt2 := &Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - Specs: []*spec.Unstructured{ - { - Meta: spec.Meta{ - Kind: chrt1.GetName(), - Namespace: resource.DefaultNamespace, - Name: faker.UUIDHyphenated(), - }, - }, - }, - } - - err := tb.Insert(chrt2) - assert.NoError(t, err) - assert.Equal(t, 0, loaded) - assert.Equal(t, 0, unloaded) - - err = tb.Insert(chrt1) - assert.NoError(t, err) - assert.Equal(t, 2, loaded) - assert.Equal(t, 0, unloaded) -} diff --git a/pkg/chart/unlinkhook.go b/pkg/chart/unlinkhook.go deleted file mode 100644 index aa294873..00000000 --- a/pkg/chart/unlinkhook.go +++ /dev/null @@ -1,37 +0,0 @@ -package chart - -// UnlinkHook defines an interface for handling the unloading of a chart. -type UnlinkHook interface { - // Unlink is called when a chart is unloaded and may return an error. - Unlink(*Chart) error -} - -// UnlinkHooks is a slice of UnloadHook, processed in reverse order. -type UnlinkHooks []UnlinkHook - -// unlinkHook wraps an unload function to implement UnloadHook. -type unlinkHook struct { - unlink func(*Chart) error -} - -var _ UnlinkHook = (UnlinkHooks)(nil) -var _ UnlinkHook = (*unlinkHook)(nil) - -// UnlinkFunc creates an UnloadHook from the given function. -func UnlinkFunc(unlink func(*Chart) error) UnlinkHook { - return &unlinkHook{unlink: unlink} -} - -func (h UnlinkHooks) Unlink(chrt *Chart) error { - for i := len(h) - 1; i >= 0; i-- { - hook := h[i] - if err := hook.Unlink(chrt); err != nil { - return err - } - } - return nil -} - -func (h *unlinkHook) Unlink(chrt *Chart) error { - return h.unlink(chrt) -} diff --git a/pkg/hook/hook.go b/pkg/hook/hook.go index 1afaeda2..87cf2f89 100644 --- a/pkg/hook/hook.go +++ b/pkg/hook/hook.go @@ -3,21 +3,16 @@ package hook import ( "sync" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/symbol" ) -// Hook represents a collection of hook functions that can be executed on charts and symbols. +// Hook represents a collection of hook functions that can be executed on symbols. type Hook struct { - linkHooks chart.LinkHooks - unlinkHooks chart.UnlinkHooks loadHooks symbol.LoadHooks unloadHooks symbol.UnloadHooks mu sync.RWMutex } -var _ chart.LinkHook = (*Hook)(nil) -var _ chart.UnlinkHook = (*Hook)(nil) var _ symbol.LoadHook = (*Hook)(nil) var _ symbol.UnloadHook = (*Hook)(nil) @@ -26,52 +21,32 @@ func New() *Hook { return &Hook{} } -// AddLinkHook adds a LinkHook function to the Hook. -func (h *Hook) AddLinkHook(hook chart.LinkHook) { - h.mu.Lock() - defer h.mu.Unlock() - - h.linkHooks = append(h.linkHooks, hook) -} - -// AddUnlinkHook adds an UnlinkHook function to the Hook. -func (h *Hook) AddUnlinkHook(hook chart.UnlinkHook) { - h.mu.Lock() - defer h.mu.Unlock() - - h.unlinkHooks = append(h.unlinkHooks, hook) -} - // AddLoadHook adds a LoadHook function to the Hook. -func (h *Hook) AddLoadHook(hook symbol.LoadHook) { +func (h *Hook) AddLoadHook(hook symbol.LoadHook) bool { h.mu.Lock() defer h.mu.Unlock() + for _, h := range h.loadHooks { + if h == hook { + return false + } + } h.loadHooks = append(h.loadHooks, hook) + return true } // AddUnloadHook adds an UnloadHook function to the Hook. -func (h *Hook) AddUnloadHook(hook symbol.UnloadHook) { +func (h *Hook) AddUnloadHook(hook symbol.UnloadHook) bool { h.mu.Lock() defer h.mu.Unlock() + for _, h := range h.unloadHooks { + if h == hook { + return false + } + } h.unloadHooks = append(h.unloadHooks, hook) -} - -// Link executes all LinkHooks registered in the Hook on the provided chart. -func (h *Hook) Link(chrt *chart.Chart) error { - h.mu.RLock() - defer h.mu.RUnlock() - - return h.linkHooks.Link(chrt) -} - -// Unlink executes all UnlinkHooks registered in the Hook on the provided chart. -func (h *Hook) Unlink(chrt *chart.Chart) error { - h.mu.RLock() - defer h.mu.RUnlock() - - return h.unlinkHooks.Unlink(chrt) + return true } // Load executes all LoadHooks registered in the Hook on the provided symbol. diff --git a/pkg/hook/hook_test.go b/pkg/hook/hook_test.go index 5ebb64a5..5f1070ac 100644 --- a/pkg/hook/hook_test.go +++ b/pkg/hook/hook_test.go @@ -3,45 +3,12 @@ package hook import ( "testing" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/symbol" "github.com/stretchr/testify/assert" ) -func TestHook_LinkHook(t *testing.T) { - hooks := New() - - count := 0 - h := chart.LinkFunc(func(_ *chart.Chart) error { - count += 1 - return nil - }) - - hooks.AddLinkHook(h) - - err := hooks.Link(&chart.Chart{}) - assert.NoError(t, err) - assert.Equal(t, 1, count) -} - -func TestHook_UnlinkHook(t *testing.T) { - hooks := New() - - count := 0 - h := chart.UnlinkFunc(func(_ *chart.Chart) error { - count += 1 - return nil - }) - - hooks.AddUnlinkHook(h) - - err := hooks.Unlink(&chart.Chart{}) - assert.NoError(t, err) - assert.Equal(t, 1, count) -} - func TestHook_LoadHook(t *testing.T) { hooks := New() @@ -53,7 +20,8 @@ func TestHook_LoadHook(t *testing.T) { return nil }) - hooks.AddLoadHook(h) + assert.True(t, hooks.AddLoadHook(h)) + assert.False(t, hooks.AddLoadHook(h)) err := hooks.Load(&symbol.Symbol{ Spec: &spec.Meta{}, @@ -74,7 +42,8 @@ func TestHook_UnloadHook(t *testing.T) { return nil }) - hooks.AddUnloadHook(h) + assert.True(t, hooks.AddUnloadHook(h)) + assert.False(t, hooks.AddUnloadHook(h)) err := hooks.Unload(&symbol.Symbol{ Spec: &spec.Meta{}, diff --git a/pkg/runtime/agent.go b/pkg/runtime/agent.go index b0bc3033..c75c965a 100644 --- a/pkg/runtime/agent.go +++ b/pkg/runtime/agent.go @@ -5,18 +5,16 @@ import ( "time" "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" "github.com/siyul-park/uniflow/pkg/process" "github.com/siyul-park/uniflow/pkg/symbol" ) -// Agent manages symbols, processes, charts, and hooks for ports and packets. +// Agent manages symbols, processes, and hooks for ports and packets. type Agent struct { symbols map[uuid.UUID]*symbol.Symbol processes map[uuid.UUID]*process.Process - charts map[uuid.UUID]*chart.Chart frames map[uuid.UUID][]*Frame inbounds map[uuid.UUID]map[string]port.OpenHook outbounds map[uuid.UUID]map[string]port.OpenHook @@ -26,15 +24,12 @@ type Agent struct { var _ symbol.LoadHook = (*Agent)(nil) var _ symbol.UnloadHook = (*Agent)(nil) -var _ chart.LinkHook = (*Agent)(nil) -var _ chart.UnlinkHook = (*Agent)(nil) // NewAgent initializes and returns a new Agent. func NewAgent() *Agent { return &Agent{ symbols: make(map[uuid.UUID]*symbol.Symbol), processes: make(map[uuid.UUID]*process.Process), - charts: make(map[uuid.UUID]*chart.Chart), frames: make(map[uuid.UUID][]*Frame), inbounds: make(map[uuid.UUID]map[string]port.OpenHook), outbounds: make(map[uuid.UUID]map[string]port.OpenHook), @@ -109,26 +104,6 @@ func (a *Agent) Process(id uuid.UUID) *process.Process { return a.processes[id] } -// Charts returns a list of all registered charts. -func (a *Agent) Charts() []*chart.Chart { - a.mu.RLock() - defer a.mu.RUnlock() - - charts := make([]*chart.Chart, 0, len(a.charts)) - for _, ch := range a.charts { - charts = append(charts, ch) - } - return charts -} - -// Chart returns a chart by UUID. -func (a *Agent) Chart(id uuid.UUID) *chart.Chart { - a.mu.RLock() - defer a.mu.RUnlock() - - return a.charts[id] -} - // Frames returns the frames associated with a specific process UUID. func (a *Agent) Frames(id uuid.UUID) []*Frame { a.mu.RLock() @@ -201,24 +176,6 @@ func (a *Agent) Unload(sym *symbol.Symbol) error { return nil } -// Link registers a chart. -func (a *Agent) Link(chrt *chart.Chart) error { - a.mu.Lock() - defer a.mu.Unlock() - - a.charts[chrt.GetID()] = chrt - return nil -} - -// Unlink removes a chart. -func (a *Agent) Unlink(chrt *chart.Chart) error { - a.mu.Lock() - defer a.mu.Unlock() - - delete(a.charts, chrt.GetID()) - return nil -} - // Close clears all symbols, processes, and registered watchers. func (a *Agent) Close() { a.mu.Lock() @@ -226,7 +183,6 @@ func (a *Agent) Close() { a.symbols = make(map[uuid.UUID]*symbol.Symbol) a.processes = make(map[uuid.UUID]*process.Process) - a.charts = make(map[uuid.UUID]*chart.Chart) a.frames = make(map[uuid.UUID][]*Frame) a.watchers = nil } diff --git a/pkg/runtime/agent_test.go b/pkg/runtime/agent_test.go index 5784c1f2..f4b7fa75 100644 --- a/pkg/runtime/agent_test.go +++ b/pkg/runtime/agent_test.go @@ -5,7 +5,6 @@ import ( "github.com/go-faker/faker/v4" "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" @@ -95,21 +94,6 @@ func TestAgent_Process(t *testing.T) { <-done } -func TestAgent_Chart(t *testing.T) { - a := NewAgent() - defer a.Close() - - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - } - - a.Link(chrt) - defer a.Unlink(chrt) - - assert.Equal(t, chrt, a.Chart(chrt.GetID())) - assert.Contains(t, a.Charts(), chrt) -} - func TestAgent_Frames(t *testing.T) { a := NewAgent() defer a.Close() diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 41202550..68b2d561 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -2,18 +2,14 @@ package runtime import ( "context" - "errors" "sync" - "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/hook" "github.com/siyul-park/uniflow/pkg/resource" "github.com/siyul-park/uniflow/pkg/scheme" "github.com/siyul-park/uniflow/pkg/spec" "github.com/siyul-park/uniflow/pkg/symbol" "github.com/siyul-park/uniflow/pkg/value" - "golang.org/x/exp/slices" ) // Config defines configuration options for the Runtime. @@ -24,7 +20,6 @@ type Config struct { Scheme *scheme.Scheme // Scheme defines the scheme and behaviors for symbols. SpecStore spec.Store // SpecStore is responsible for persisting specifications. ValueStore value.Store // ValueStore is responsible for persisting values. - ChartStore chart.Store // ChartStore is responsible for persisting charts. } // Runtime represents an environment for executing Workflows. @@ -33,14 +28,10 @@ type Runtime struct { scheme *scheme.Scheme specStore spec.Store valueStore value.Store - chartStore chart.Store specStream spec.Stream valueStream value.Stream - chartStream chart.Stream symbolTable *symbol.Table symbolLoader *symbol.Loader - chartTable *chart.Table - chartLoader *chart.Loader mu sync.RWMutex } @@ -61,9 +52,6 @@ func New(config Config) *Runtime { if config.ValueStore == nil { config.ValueStore = value.NewStore() } - if config.ChartStore == nil { - config.ChartStore = chart.NewStore() - } config.Hook.AddLoadHook(symbol.LoadListenerHook(config.Hook)) config.Hook.AddUnloadHook(symbol.UnloadListenerHook(config.Hook)) @@ -80,48 +68,19 @@ func New(config Config) *Runtime { ValueStore: config.ValueStore, }) - chartLinker := chart.NewLinker(config.Scheme) - - config.Hook.AddLinkHook(chartLinker) - config.Hook.AddUnlinkHook(chartLinker) - - chartTable := chart.NewTable(chart.TableOption{ - LinkHooks: []chart.LinkHook{config.Hook}, - UnlinkHooks: []chart.UnlinkHook{config.Hook}, - }) - chartLoader := chart.NewLoader(chart.LoaderConfig{ - Table: chartTable, - ChartStore: config.ChartStore, - ValueStore: config.ValueStore, - }) - - for _, kind := range config.Scheme.Kinds() { - _ = chartTable.Insert(&chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: config.Namespace, - Name: kind, - }) - } - return &Runtime{ namespace: config.Namespace, scheme: config.Scheme, specStore: config.SpecStore, valueStore: config.ValueStore, - chartStore: config.ChartStore, symbolTable: symbolTable, symbolLoader: symbolLoader, - chartTable: chartTable, - chartLoader: chartLoader, } } // Load loads symbols from the spec store into the symbol table. func (r *Runtime) Load(ctx context.Context) error { - var errs []error - errs = append(errs, r.chartLoader.Load(ctx, &chart.Chart{Namespace: r.namespace})) - errs = append(errs, r.symbolLoader.Load(ctx, &spec.Meta{Namespace: r.namespace})) - return errors.Join(errs...) + return r.symbolLoader.Load(ctx, &spec.Meta{Namespace: r.namespace}) } // Watch sets up watchers for specification and value changes. @@ -151,17 +110,6 @@ func (r *Runtime) Watch(ctx context.Context) error { } r.valueStream = valueStream - if r.chartStream != nil { - if err := r.chartStream.Close(); err != nil { - return err - } - } - chartStream, err := r.chartStore.Watch(ctx, &chart.Chart{Namespace: r.namespace}) - if err != nil { - return err - } - r.chartStream = chartStream - return nil } @@ -171,11 +119,10 @@ func (r *Runtime) Reconcile(ctx context.Context) error { specStream := r.specStream valueStream := r.valueStream - chartStream := r.chartStream r.mu.RUnlock() - if specStream == nil || valueStream == nil || chartStream == nil { + if specStream == nil || valueStream == nil { return nil } @@ -224,39 +171,6 @@ func (r *Runtime) Reconcile(ctx context.Context) error { } } - _ = r.symbolLoader.Load(ctx, specs...) - case event, ok := <-chartStream.Next(): - if !ok { - return nil - } - - charts := r.chartTable.Links(event.ID) - if len(charts) == 0 { - var err error - charts, err = r.chartStore.Load(ctx, &chart.Chart{ID: event.ID}) - if err != nil { - return err - } - } - - kinds := make([]string, 0, len(charts)) - for _, chrt := range charts { - kinds = append(kinds, chrt.GetName()) - } - - var specs []spec.Spec - for _, id := range r.symbolTable.Keys() { - sb := r.symbolTable.Lookup(id) - if sb != nil && slices.Contains(kinds, sb.Kind()) { - specs = append(specs, sb.Spec) - } - } - - for _, sp := range specs { - _, _ = r.symbolTable.Free(sp.GetID()) - } - - _ = r.chartLoader.Load(ctx, &chart.Chart{ID: event.ID}) _ = r.symbolLoader.Load(ctx, specs...) } } @@ -279,15 +193,5 @@ func (r *Runtime) Close() error { } r.valueStream = nil } - if r.chartStream != nil { - if err := r.chartStream.Close(); err != nil { - return err - } - r.chartStream = nil - } - - if err := r.chartTable.Close(); err != nil { - return err - } return r.symbolTable.Close() } diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go index 1546bc41..fbb6c60e 100644 --- a/pkg/runtime/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -7,7 +7,6 @@ import ( "github.com/go-faker/faker/v4" "github.com/gofrs/uuid" - "github.com/siyul-park/uniflow/pkg/chart" "github.com/siyul-park/uniflow/pkg/hook" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/resource" @@ -32,13 +31,11 @@ func TestRuntime_Load(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() r := New(Config{ Scheme: s, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) defer r.Close() @@ -68,7 +65,6 @@ func TestRuntime_Reconcile(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() h := hook.New() symbols := make(chan *symbol.Symbol) @@ -87,7 +83,6 @@ func TestRuntime_Reconcile(t *testing.T) { Hook: h, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) defer r.Close() @@ -135,7 +130,6 @@ func TestRuntime_Reconcile(t *testing.T) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() h := hook.New() symbols := make(chan *symbol.Symbol) @@ -154,7 +148,6 @@ func TestRuntime_Reconcile(t *testing.T) { Hook: h, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) err := r.Watch(ctx) @@ -200,74 +193,6 @@ func TestRuntime_Reconcile(t *testing.T) { assert.NoError(t, ctx.Err()) } }) - - t.Run("Chart", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.TODO(), time.Second) - defer cancel() - - s := scheme.New() - kind := faker.UUIDHyphenated() - - specStore := spec.NewStore() - valueStore := value.NewStore() - chartStore := chart.NewStore() - - h := hook.New() - symbols := make(chan *symbol.Symbol) - - h.AddLoadHook(symbol.LoadFunc(func(sb *symbol.Symbol) error { - symbols <- sb - return nil - })) - h.AddUnloadHook(symbol.UnloadFunc(func(sb *symbol.Symbol) error { - symbols <- sb - return nil - })) - - r := New(Config{ - Scheme: s, - Hook: h, - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - }) - defer r.Close() - - err := r.Watch(ctx) - assert.NoError(t, err) - - go r.Reconcile(ctx) - - meta := &spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: kind, - Namespace: resource.DefaultNamespace, - } - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: kind, - } - - specStore.Store(ctx, meta) - chartStore.Store(ctx, chrt) - - select { - case sb := <-symbols: - assert.Equal(t, meta.GetID(), sb.ID()) - case <-ctx.Done(): - assert.NoError(t, ctx.Err()) - } - - chartStore.Delete(ctx, chrt) - - select { - case sb := <-symbols: - assert.Equal(t, meta.GetID(), sb.ID()) - case <-ctx.Done(): - assert.NoError(t, ctx.Err()) - } - }) } func BenchmarkRuntime_Reconcile(b *testing.B) { @@ -284,7 +209,6 @@ func BenchmarkRuntime_Reconcile(b *testing.B) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() h := hook.New() symbols := make(chan *symbol.Symbol) @@ -299,7 +223,6 @@ func BenchmarkRuntime_Reconcile(b *testing.B) { Hook: h, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) defer r.Close() @@ -337,7 +260,6 @@ func BenchmarkRuntime_Reconcile(b *testing.B) { specStore := spec.NewStore() valueStore := value.NewStore() - chartStore := chart.NewStore() h := hook.New() symbols := make(chan *symbol.Symbol) @@ -352,7 +274,6 @@ func BenchmarkRuntime_Reconcile(b *testing.B) { Hook: h, SpecStore: specStore, ValueStore: valueStore, - ChartStore: chartStore, }) err := r.Watch(ctx) @@ -388,58 +309,4 @@ func BenchmarkRuntime_Reconcile(b *testing.B) { } } }) - - b.Run("Chart", func(b *testing.B) { - ctx := context.TODO() - - s := scheme.New() - kind := faker.UUIDHyphenated() - - specStore := spec.NewStore() - valueStore := value.NewStore() - chartStore := chart.NewStore() - - h := hook.New() - symbols := make(chan *symbol.Symbol) - - h.AddLoadHook(symbol.LoadFunc(func(sb *symbol.Symbol) error { - symbols <- sb - return nil - })) - - r := New(Config{ - Scheme: s, - Hook: h, - SpecStore: specStore, - ValueStore: valueStore, - ChartStore: chartStore, - }) - defer r.Close() - - err := r.Watch(ctx) - assert.NoError(b, err) - - go r.Reconcile(ctx) - - for i := 0; i < b.N; i++ { - meta := &spec.Meta{ - ID: uuid.Must(uuid.NewV7()), - Kind: kind, - Namespace: resource.DefaultNamespace, - } - chrt := &chart.Chart{ - ID: uuid.Must(uuid.NewV7()), - Namespace: resource.DefaultNamespace, - Name: kind, - } - - specStore.Store(ctx, meta) - chartStore.Store(ctx, chrt) - - select { - case <-symbols: - case <-ctx.Done(): - } - } - }) } diff --git a/pkg/symbol/loadhook.go b/pkg/symbol/loadhook.go index 35f9446e..6b65d007 100644 --- a/pkg/symbol/loadhook.go +++ b/pkg/symbol/loadhook.go @@ -19,8 +19,8 @@ type loadHook struct { fn func(*Symbol) error } -var _ LoadHook = (*loadHook)(nil) var _ LoadHook = (LoadHooks)(nil) +var _ LoadHook = (*loadHook)(nil) // LoadFunc wraps a function as a LoadHook. func LoadFunc(fn func(*Symbol) error) LoadHook { @@ -47,8 +47,8 @@ func LoadListenerHook(hook LoadHook) LoadHook { } // Load executes all LoadHooks sequentially. -func (hooks LoadHooks) Load(symbol *Symbol) error { - for _, hook := range hooks { +func (h LoadHooks) Load(symbol *Symbol) error { + for _, hook := range h { if err := hook.Load(symbol); err != nil { return err } diff --git a/pkg/symbol/unloadhook.go b/pkg/symbol/unloadhook.go index db7714f4..889aa795 100644 --- a/pkg/symbol/unloadhook.go +++ b/pkg/symbol/unloadhook.go @@ -19,8 +19,8 @@ type unloadHook struct { fn func(*Symbol) error } -var _ UnloadHook = (*unloadHook)(nil) var _ UnloadHook = (UnloadHooks)(nil) +var _ UnloadHook = (*unloadHook)(nil) // UnloadFunc wraps a function as an UnloadHook. func UnloadFunc(fn func(*Symbol) error) UnloadHook { @@ -47,9 +47,9 @@ func UnloadListenerHook(hook UnloadHook) UnloadHook { } // Unload executes all UnloadHooks in reverse order. -func (hooks UnloadHooks) Unload(symbol *Symbol) error { - for i := len(hooks) - 1; i >= 0; i-- { - if err := hooks[i].Unload(symbol); err != nil { +func (h UnloadHooks) Unload(symbol *Symbol) error { + for i := len(h) - 1; i >= 0; i-- { + if err := h[i].Unload(symbol); err != nil { return err } }