From 6ee423e35f90157bfa29e6f284d88c95f00215e2 Mon Sep 17 00:00:00 2001 From: kyasbal Date: Mon, 6 Oct 2025 17:04:52 +0900 Subject: [PATCH 1/3] Rename inspectiontaskbase.PreviousTaskResult to CachableTaskResult (#290) --- pkg/core/inspection/taskbase/cached_task.go | 10 +++++----- pkg/core/inspection/taskbase/cached_task_test.go | 8 ++++---- .../impl/autocompletecomposerclusternames_task.go | 12 ++++++------ .../autocompletecomposerenvironmentnames_task.go | 10 +++++----- .../autocompletegdcvforbaremetalclusternames_task.go | 10 +++++----- .../autocompletegdcvforvmwareclusternames_task.go | 10 +++++----- .../impl/autocompletegkeclusternames_task.go | 10 +++++----- .../googlecloudclustergkeonaws/impl/autocomplete.go | 10 +++++----- .../impl/autocomplete.go | 10 +++++----- .../impl/autocompletelocation_task.go | 6 +++--- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/pkg/core/inspection/taskbase/cached_task.go b/pkg/core/inspection/taskbase/cached_task.go index e488083e6..f31ed0a64 100644 --- a/pkg/core/inspection/taskbase/cached_task.go +++ b/pkg/core/inspection/taskbase/cached_task.go @@ -25,8 +25,8 @@ import ( inspectioncore_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/inspectioncore/contract" ) -// PreviousTaskResult is the combination of the cached value and a digest of its dependency. -type PreviousTaskResult[T any] struct { +// CachableTaskResult is the combination of the cached value and a digest of its dependency. +type CachableTaskResult[T any] struct { // Value is the value used previous run. Value T // DependencyDigest is a string representation of digest of its inputs. @@ -35,11 +35,11 @@ type PreviousTaskResult[T any] struct { } // NewCachedTask generates a task which can reuse the value last time. -func NewCachedTask[T any](taskID taskid.TaskImplementationID[T], depdendencies []taskid.UntypedTaskReference, f func(ctx context.Context, prevValue PreviousTaskResult[T]) (PreviousTaskResult[T], error), labelOpt ...coretask.LabelOpt) coretask.Task[T] { +func NewCachedTask[T any](taskID taskid.TaskImplementationID[T], depdendencies []taskid.UntypedTaskReference, f func(ctx context.Context, prevValue CachableTaskResult[T]) (CachableTaskResult[T], error), labelOpt ...coretask.LabelOpt) coretask.Task[T] { return coretask.NewTask(taskID, depdendencies, func(ctx context.Context) (T, error) { inspectionSharedMap := khictx.MustGetValue(ctx, inspectioncore_contract.GlobalSharedMap) - cacheKey := typedmap.NewTypedKey[PreviousTaskResult[T]](fmt.Sprintf("cached_result-%s", taskID.String())) - cachedResult := typedmap.GetOrDefault(inspectionSharedMap, cacheKey, PreviousTaskResult[T]{ + cacheKey := typedmap.NewTypedKey[CachableTaskResult[T]](fmt.Sprintf("cached_result-%s", taskID.String())) + cachedResult := typedmap.GetOrDefault(inspectionSharedMap, cacheKey, CachableTaskResult[T]{ Value: *new(T), DependencyDigest: "", }) diff --git a/pkg/core/inspection/taskbase/cached_task_test.go b/pkg/core/inspection/taskbase/cached_task_test.go index 94b0d4191..9207184a9 100644 --- a/pkg/core/inspection/taskbase/cached_task_test.go +++ b/pkg/core/inspection/taskbase/cached_task_test.go @@ -25,11 +25,11 @@ import ( ) func TestCachedTask(t *testing.T) { - prevValues := []PreviousTaskResult[string]{} + prevValues := []CachableTaskResult[string]{} testTaskID := taskid.NewDefaultImplementationID[string]("foo") - task := NewCachedTask(testTaskID, []taskid.UntypedTaskReference{}, func(ctx context.Context, prevValue PreviousTaskResult[string]) (PreviousTaskResult[string], error) { + task := NewCachedTask(testTaskID, []taskid.UntypedTaskReference{}, func(ctx context.Context, prevValue CachableTaskResult[string]) (CachableTaskResult[string], error) { prevValues = append(prevValues, prevValue) - return PreviousTaskResult[string]{ + return CachableTaskResult[string]{ Value: "foo", DependencyDigest: "foo", }, nil @@ -45,7 +45,7 @@ func TestCachedTask(t *testing.T) { t.Errorf("unexpected task error result %v", err) } - if diff := cmp.Diff(prevValues, []PreviousTaskResult[string]{ + if diff := cmp.Diff(prevValues, []CachableTaskResult[string]{ { Value: "", DependencyDigest: "", diff --git a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go index 4212bc14f..84de71b5b 100644 --- a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go @@ -33,11 +33,11 @@ import ( var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustercomposer_contract.AutocompleteComposerClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), googlecloudclustercomposer_contract.InputComposerEnvironmentNameTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // when the user is inputing these information, abort isWIP := projectID == "" || environment == "" if isWIP { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -63,7 +63,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // fetch all GKE clusters in the project clusters, err := client.GetClusters(ctx, projectID) if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -76,7 +76,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // = the gke cluster where the composer is running for _, cluster := range clusters { if cluster.ResourceLabels["goog-composer-environment"] == environment { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{cluster.Name}, @@ -85,7 +85,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog } } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go index b6f2aeb39..f3b4031b4 100644 --- a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go +++ b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go @@ -30,10 +30,10 @@ import ( var AutocompleteComposerEnvironmentNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustercomposer_contract.AutocompleteComposerEnvironmentNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputLocationsTaskID.Ref(), googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[[]string]) (inspectiontaskbase.PreviousTaskResult[[]string], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[[]string]) (inspectiontaskbase.CachableTaskResult[[]string], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[[]string]{}, err + return inspectiontaskbase.CachableTaskResult[[]string]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) location := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputLocationsTaskID.Ref()) @@ -47,17 +47,17 @@ var AutocompleteComposerEnvironmentNamesTask = inspectiontaskbase.NewCachedTask( clusterNames, err := client.GetComposerEnvironmentNames(ctx, projectID, location) if err != nil { // Failed to read the composer environments in the (project,location) - return inspectiontaskbase.PreviousTaskResult[[]string]{ + return inspectiontaskbase.CachableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, }, nil } - return inspectiontaskbase.PreviousTaskResult[[]string]{ + return inspectiontaskbase.CachableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: clusterNames, }, nil } - return inspectiontaskbase.PreviousTaskResult[[]string]{ + return inspectiontaskbase.CachableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, }, nil diff --git a/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go b/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go index affccce7b..c9984b8df 100644 --- a/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGDCVForBaremetalClusterNamesTask is a task that provides autocomplete suggestions for GDCV for Baremetal cluster names. var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergdcbaremetal_contract.AutocompleteGDCVForBaremetalClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT clusterNames, err := client.GetAnthosOnBaremetalClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go b/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go index 424469c8a..110c2268b 100644 --- a/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGDCVForVMWareClusterNamesTask is a task that provides autocomplete suggestions for GDCV for VMWare cluster names. var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergdcvmware_contract.AutocompleteGDCVForVMWareClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask clusterNames, err := client.GetAnthosOnVMWareClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go b/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go index 888776403..278c27977 100644 --- a/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEClusterNamesTask is a task that provides autocomplete suggestions for GKE cluster names. var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergke_contract.AutocompleteGKEClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo clusterNames, err := client.GetClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go b/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go index 76a48406c..b5ef7935d 100644 --- a/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go +++ b/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEOnAWSClusterNames is a task that provides a list of GKE on AWS cluster names for autocompletion. var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecloudclustergkeonaws_contract.AutocompleteGKEOnAWSClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl clusterNames, err := client.GetAnthosAWSClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go b/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go index 62be66336..31fd8e304 100644 --- a/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go +++ b/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEOnAzureClusterNamesTask is a task that provides a list of GKE on Azure cluster names for autocompletion. var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergkeonazure_contract.AutocompleteGKEOnAzureClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go clusterNames, err := client.GetAnthosAzureClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go }, }, nil } - return inspectiontaskbase.PreviousTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go b/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go index dd5d47e2a..e2fb0c932 100644 --- a/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go +++ b/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go @@ -30,10 +30,10 @@ var AutocompleteLocationTask = inspectiontaskbase.NewCachedTask(googlecloudcommo []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), // for API restriction }, - func(ctx context.Context, prevValue inspectiontaskbase.PreviousTaskResult[[]string]) (inspectiontaskbase.PreviousTaskResult[[]string], error) { + func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[[]string]) (inspectiontaskbase.CachableTaskResult[[]string], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.PreviousTaskResult[[]string]{}, err + return inspectiontaskbase.CachableTaskResult[[]string]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) dependencyDigest := fmt.Sprintf("location-%s", projectID) @@ -42,7 +42,7 @@ var AutocompleteLocationTask = inspectiontaskbase.NewCachedTask(googlecloudcommo return prevValue, nil } - defaultResult := inspectiontaskbase.PreviousTaskResult[[]string]{ + defaultResult := inspectiontaskbase.CachableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, } From e6af2d13eaf87dca6eb32015288cc9824de17535 Mon Sep 17 00:00:00 2001 From: kyasbal Date: Mon, 6 Oct 2025 17:17:58 +0900 Subject: [PATCH 2/3] Fix typo on #290 (#292) --- pkg/core/inspection/taskbase/cached_task.go | 10 +++++----- pkg/core/inspection/taskbase/cached_task_test.go | 8 ++++---- .../impl/autocompletecomposerclusternames_task.go | 12 ++++++------ .../autocompletecomposerenvironmentnames_task.go | 10 +++++----- .../autocompletegdcvforbaremetalclusternames_task.go | 10 +++++----- .../autocompletegdcvforvmwareclusternames_task.go | 10 +++++----- .../impl/autocompletegkeclusternames_task.go | 10 +++++----- .../googlecloudclustergkeonaws/impl/autocomplete.go | 10 +++++----- .../impl/autocomplete.go | 10 +++++----- .../impl/autocompletelocation_task.go | 6 +++--- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/pkg/core/inspection/taskbase/cached_task.go b/pkg/core/inspection/taskbase/cached_task.go index f31ed0a64..fb3f80433 100644 --- a/pkg/core/inspection/taskbase/cached_task.go +++ b/pkg/core/inspection/taskbase/cached_task.go @@ -25,8 +25,8 @@ import ( inspectioncore_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/inspectioncore/contract" ) -// CachableTaskResult is the combination of the cached value and a digest of its dependency. -type CachableTaskResult[T any] struct { +// CacheableTaskResult is the combination of the cached value and a digest of its dependency. +type CacheableTaskResult[T any] struct { // Value is the value used previous run. Value T // DependencyDigest is a string representation of digest of its inputs. @@ -35,11 +35,11 @@ type CachableTaskResult[T any] struct { } // NewCachedTask generates a task which can reuse the value last time. -func NewCachedTask[T any](taskID taskid.TaskImplementationID[T], depdendencies []taskid.UntypedTaskReference, f func(ctx context.Context, prevValue CachableTaskResult[T]) (CachableTaskResult[T], error), labelOpt ...coretask.LabelOpt) coretask.Task[T] { +func NewCachedTask[T any](taskID taskid.TaskImplementationID[T], depdendencies []taskid.UntypedTaskReference, f func(ctx context.Context, prevValue CacheableTaskResult[T]) (CacheableTaskResult[T], error), labelOpt ...coretask.LabelOpt) coretask.Task[T] { return coretask.NewTask(taskID, depdendencies, func(ctx context.Context) (T, error) { inspectionSharedMap := khictx.MustGetValue(ctx, inspectioncore_contract.GlobalSharedMap) - cacheKey := typedmap.NewTypedKey[CachableTaskResult[T]](fmt.Sprintf("cached_result-%s", taskID.String())) - cachedResult := typedmap.GetOrDefault(inspectionSharedMap, cacheKey, CachableTaskResult[T]{ + cacheKey := typedmap.NewTypedKey[CacheableTaskResult[T]](fmt.Sprintf("cached_result-%s", taskID.String())) + cachedResult := typedmap.GetOrDefault(inspectionSharedMap, cacheKey, CacheableTaskResult[T]{ Value: *new(T), DependencyDigest: "", }) diff --git a/pkg/core/inspection/taskbase/cached_task_test.go b/pkg/core/inspection/taskbase/cached_task_test.go index 9207184a9..38df7cb4d 100644 --- a/pkg/core/inspection/taskbase/cached_task_test.go +++ b/pkg/core/inspection/taskbase/cached_task_test.go @@ -25,11 +25,11 @@ import ( ) func TestCachedTask(t *testing.T) { - prevValues := []CachableTaskResult[string]{} + prevValues := []CacheableTaskResult[string]{} testTaskID := taskid.NewDefaultImplementationID[string]("foo") - task := NewCachedTask(testTaskID, []taskid.UntypedTaskReference{}, func(ctx context.Context, prevValue CachableTaskResult[string]) (CachableTaskResult[string], error) { + task := NewCachedTask(testTaskID, []taskid.UntypedTaskReference{}, func(ctx context.Context, prevValue CacheableTaskResult[string]) (CacheableTaskResult[string], error) { prevValues = append(prevValues, prevValue) - return CachableTaskResult[string]{ + return CacheableTaskResult[string]{ Value: "foo", DependencyDigest: "foo", }, nil @@ -45,7 +45,7 @@ func TestCachedTask(t *testing.T) { t.Errorf("unexpected task error result %v", err) } - if diff := cmp.Diff(prevValues, []CachableTaskResult[string]{ + if diff := cmp.Diff(prevValues, []CacheableTaskResult[string]{ { Value: "", DependencyDigest: "", diff --git a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go index 84de71b5b..fd741077d 100644 --- a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go @@ -33,11 +33,11 @@ import ( var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustercomposer_contract.AutocompleteComposerClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), googlecloudclustercomposer_contract.InputComposerEnvironmentNameTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // when the user is inputing these information, abort isWIP := projectID == "" || environment == "" if isWIP { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -63,7 +63,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // fetch all GKE clusters in the project clusters, err := client.GetClusters(ctx, projectID) if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -76,7 +76,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog // = the gke cluster where the composer is running for _, cluster := range clusters { if cluster.ResourceLabels["goog-composer-environment"] == environment { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{cluster.Name}, @@ -85,7 +85,7 @@ var AutocompleteComposerClusterNamesTask = inspectiontaskbase.NewCachedTask(goog } } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: dependencyDigest, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go index f3b4031b4..2e8a32925 100644 --- a/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go +++ b/pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerenvironmentnames_task.go @@ -30,10 +30,10 @@ import ( var AutocompleteComposerEnvironmentNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustercomposer_contract.AutocompleteComposerEnvironmentNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputLocationsTaskID.Ref(), googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[[]string]) (inspectiontaskbase.CachableTaskResult[[]string], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[[]string]) (inspectiontaskbase.CacheableTaskResult[[]string], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[[]string]{}, err + return inspectiontaskbase.CacheableTaskResult[[]string]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) location := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputLocationsTaskID.Ref()) @@ -47,17 +47,17 @@ var AutocompleteComposerEnvironmentNamesTask = inspectiontaskbase.NewCachedTask( clusterNames, err := client.GetComposerEnvironmentNames(ctx, projectID, location) if err != nil { // Failed to read the composer environments in the (project,location) - return inspectiontaskbase.CachableTaskResult[[]string]{ + return inspectiontaskbase.CacheableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, }, nil } - return inspectiontaskbase.CachableTaskResult[[]string]{ + return inspectiontaskbase.CacheableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: clusterNames, }, nil } - return inspectiontaskbase.CachableTaskResult[[]string]{ + return inspectiontaskbase.CacheableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, }, nil diff --git a/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go b/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go index c9984b8df..8bc79c19b 100644 --- a/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergdcbaremetal/impl/autocompletegdcvforbaremetalclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGDCVForBaremetalClusterNamesTask is a task that provides autocomplete suggestions for GDCV for Baremetal cluster names. var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergdcbaremetal_contract.AutocompleteGDCVForBaremetalClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT clusterNames, err := client.GetAnthosOnBaremetalClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGDCVForBaremetalClusterNamesTask = inspectiontaskbase.NewCachedT }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go b/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go index 110c2268b..daeafc5c9 100644 --- a/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergdcvmware/impl/autocompletegdcvforvmwareclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGDCVForVMWareClusterNamesTask is a task that provides autocomplete suggestions for GDCV for VMWare cluster names. var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergdcvmware_contract.AutocompleteGDCVForVMWareClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask clusterNames, err := client.GetAnthosOnVMWareClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGDCVForVMWareClusterNamesTask = inspectiontaskbase.NewCachedTask }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go b/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go index 278c27977..3ab5ff778 100644 --- a/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go +++ b/pkg/task/inspection/googlecloudclustergke/impl/autocompletegkeclusternames_task.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEClusterNamesTask is a task that provides autocomplete suggestions for GKE cluster names. var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergke_contract.AutocompleteGKEClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo clusterNames, err := client.GetClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEClusterNamesTask = inspectiontaskbase.NewCachedTask(googleclo }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go b/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go index b5ef7935d..446775c1c 100644 --- a/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go +++ b/pkg/task/inspection/googlecloudclustergkeonaws/impl/autocomplete.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEOnAWSClusterNames is a task that provides a list of GKE on AWS cluster names for autocompletion. var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecloudclustergkeonaws_contract.AutocompleteGKEOnAWSClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl clusterNames, err := client.GetAnthosAWSClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEOnAWSClusterNames = inspectiontaskbase.NewCachedTask(googlecl }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go b/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go index 31fd8e304..69742c247 100644 --- a/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go +++ b/pkg/task/inspection/googlecloudclustergkeonazure/impl/autocomplete.go @@ -32,10 +32,10 @@ import ( // AutocompleteGKEOnAzureClusterNamesTask is a task that provides a list of GKE on Azure cluster names for autocompletion. var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(googlecloudclustergkeonazure_contract.AutocompleteGKEOnAzureClusterNamesTaskID, []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), -}, func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { +}, func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]) (inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{}, nil } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) @@ -47,7 +47,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go clusterNames, err := client.GetAnthosAzureClusterNames(ctx, projectID) if err != nil { slog.WarnContext(ctx, fmt.Sprintf("Failed to read the cluster names in the project %s\n%s", projectID, err)) - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, @@ -55,7 +55,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: clusterNames, @@ -63,7 +63,7 @@ var AutocompleteGKEOnAzureClusterNamesTask = inspectiontaskbase.NewCachedTask(go }, }, nil } - return inspectiontaskbase.CachableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ + return inspectiontaskbase.CacheableTaskResult[*googlecloudk8scommon_contract.AutocompleteClusterNameList]{ DependencyDigest: projectID, Value: &googlecloudk8scommon_contract.AutocompleteClusterNameList{ ClusterNames: []string{}, diff --git a/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go b/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go index e2fb0c932..1b6890a33 100644 --- a/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go +++ b/pkg/task/inspection/googlecloudcommon/impl/autocompletelocation_task.go @@ -30,10 +30,10 @@ var AutocompleteLocationTask = inspectiontaskbase.NewCachedTask(googlecloudcommo []taskid.UntypedTaskReference{ googlecloudcommon_contract.InputProjectIdTaskID.Ref(), // for API restriction }, - func(ctx context.Context, prevValue inspectiontaskbase.CachableTaskResult[[]string]) (inspectiontaskbase.CachableTaskResult[[]string], error) { + func(ctx context.Context, prevValue inspectiontaskbase.CacheableTaskResult[[]string]) (inspectiontaskbase.CacheableTaskResult[[]string], error) { client, err := googlecloudapi.DefaultGCPClientFactory.NewClient() if err != nil { - return inspectiontaskbase.CachableTaskResult[[]string]{}, err + return inspectiontaskbase.CacheableTaskResult[[]string]{}, err } projectID := coretask.GetTaskResult(ctx, googlecloudcommon_contract.InputProjectIdTaskID.Ref()) dependencyDigest := fmt.Sprintf("location-%s", projectID) @@ -42,7 +42,7 @@ var AutocompleteLocationTask = inspectiontaskbase.NewCachedTask(googlecloudcommo return prevValue, nil } - defaultResult := inspectiontaskbase.CachableTaskResult[[]string]{ + defaultResult := inspectiontaskbase.CacheableTaskResult[[]string]{ DependencyDigest: dependencyDigest, Value: []string{}, } From 7c2e96a3178006a85f4b145660ee15d7c7134f8b Mon Sep 17 00:00:00 2001 From: kyasbal Date: Thu, 9 Oct 2025 20:32:23 +0900 Subject: [PATCH 3/3] feat(server): Add ServerFactory type to configure the gin.Engine (#288) * feat(server): Add ServerFactory type to configure the gin.Engine instance easier in the initialization phase Several KHI feature sometimes needs to register API handler on gin.Engine but it was difficult before. This makes the initialization code under cmd/kubernetes-history-inspector harder. This change introduced a new type ServerFactory used for instancing a new gin.Engine instance. This type receives Option parameters to extend the server instance. This allows user to extend its behavior just by adding new init() function on cmd/kubernetes-history-inspector or each task package folders. * fix points pointed out by gemini-code-review --- cmd/kubernetes-history-inspector/main.go | 25 +++- pkg/server/option/option.go | 127 ++++++++++++++++++ pkg/server/option/option_test.go | 163 +++++++++++++++++++++++ pkg/server/server.go | 23 +--- pkg/server/server_test.go | 13 +- pkg/server/serverfactory.go | 54 ++++++++ pkg/server/serverfactory_test.go | 130 ++++++++++++++++++ 7 files changed, 508 insertions(+), 27 deletions(-) create mode 100644 pkg/server/option/option.go create mode 100644 pkg/server/option/option_test.go create mode 100644 pkg/server/serverfactory.go create mode 100644 pkg/server/serverfactory_test.go diff --git a/cmd/kubernetes-history-inspector/main.go b/cmd/kubernetes-history-inspector/main.go index 61092c22b..599d71b8a 100644 --- a/cmd/kubernetes-history-inspector/main.go +++ b/cmd/kubernetes-history-inspector/main.go @@ -37,7 +37,10 @@ import ( "github.com/GoogleCloudPlatform/khi/pkg/model/k8s" "github.com/GoogleCloudPlatform/khi/pkg/parameters" "github.com/GoogleCloudPlatform/khi/pkg/server" + "github.com/GoogleCloudPlatform/khi/pkg/server/option" "github.com/GoogleCloudPlatform/khi/pkg/server/upload" + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" inspectioncore_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/inspectioncore/contract" @@ -152,6 +155,21 @@ func run() int { slog.Info("Starting Kubernetes History Inspector server...") + // Setting up options or parameters needed to instanciate gin.Engine + serverMode := gin.ReleaseMode + server.DefaultServerFactory.AddOptions(option.Required()) + + corsConfig := cors.DefaultConfig() + corsConfig.AllowAllOrigins = true + server.DefaultServerFactory.AddOptions(option.CORS(corsConfig)) + + if *parameters.Debug.Verbose { + server.DefaultServerFactory.AddOptions( + option.AccessLog("/api/v3/inspection", "/api/v3/popup"), // ignoreing noisy paths + ) + serverMode = gin.DebugMode + } + uploadFileStoreFolder := "/tmp" if parameters.Common.UploadFileStoreFolder != nil { @@ -167,7 +185,12 @@ func run() int { ServerBasePath: *parameters.Server.BasePath, UploadFileStore: upload.DefaultUploadFileStore, } - engine := server.CreateKHIServer(inspectionServer, &config) + engine, err := server.DefaultServerFactory.CreateInstance(serverMode) + if err != nil { + slog.Error(fmt.Sprintf("Failed to create a server instance\n%v", err)) + return 1 + } + engine = server.CreateKHIServer(engine, inspectionServer, &config) if parameters.Auth.OAuthEnabled() { err := accesstoken.DefaultOAuthTokenResolver.SetServer(engine) diff --git a/pkg/server/option/option.go b/pkg/server/option/option.go new file mode 100644 index 000000000..1e6220cd9 --- /dev/null +++ b/pkg/server/option/option.go @@ -0,0 +1,127 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "slices" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" +) + +// Option defines an interface for configuring a Gin engine. +type Option interface { + // ID returns a unique identifier for the option. + ID() string + // Order returns the order in which this option should be applied relative to other options. + Order() int + // Apply applies the option's configuration to the given Gin engine. + // It returns an error if the application fails. + Apply(engine *gin.Engine) error +} + +func ApplyOptions(engine *gin.Engine, options []Option) error { + slices.SortFunc(options, func(a, b Option) int { return a.Order() - b.Order() }) + for _, option := range options { + err := option.Apply(engine) + if err != nil { + return err + } + } + return nil +} + +// requiredOption is an Option implementation for setting several required middleware in KHI. +type requiredOption struct { +} + +// Required creates a new Option to set several required middlewares and gin server mode. +func Required() Option { + return &requiredOption{} +} + +func (s *requiredOption) ID() string { + return "required" +} + +// Order returns the application order for the server mode option. +func (s *requiredOption) Order() int { + return 0 +} + +// Apply adds required middlewares (currently the recovery is the only middleware.) +func (s *requiredOption) Apply(engine *gin.Engine) error { + engine.Use(gin.Recovery()) + return nil +} + +var _ Option = (*requiredOption)(nil) + +// corsOption is an Option implementation for enabling CORS. +type corsOption struct { + corsConfig cors.Config +} + +// CORS creates a new Option to enable CORS. +func CORS(config cors.Config) Option { + return &corsOption{config} +} + +func (c *corsOption) ID() string { + return "cors" +} + +// Order returns the application order for the CORS option. +func (c *corsOption) Order() int { + return 1 +} + +// Apply configures the Gin engine to use the gin-contrib/cors middleware with all origins allowed. +func (c *corsOption) Apply(engine *gin.Engine) error { + engine.Use(cors.New(c.corsConfig)) + return nil +} + +var _ Option = (*corsOption)(nil) + +type accessLogOption struct { + ignoredPath []string +} + +// AccessLog creates a new Option to log access logs with ignoreing the provided paths. +func AccessLog(ignoredPath ...string) Option { + return &accessLogOption{ + ignoredPath: ignoredPath, + } +} + +// Apply implements Option. +func (l *accessLogOption) Apply(engine *gin.Engine) error { + engine.Use(gin.LoggerWithConfig(gin.LoggerConfig{ + SkipPaths: l.ignoredPath, + })) + return nil +} + +// Order implements Option. +func (l *accessLogOption) Order() int { + return 2 +} + +func (l *accessLogOption) ID() string { + return "access-log" +} + +var _ Option = (*accessLogOption)(nil) diff --git a/pkg/server/option/option_test.go b/pkg/server/option/option_test.go new file mode 100644 index 000000000..487e8d6f3 --- /dev/null +++ b/pkg/server/option/option_test.go @@ -0,0 +1,163 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "errors" + "fmt" + "net/http/httptest" + "testing" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" +) + +// mockOption is a helper for testing. +type mockOption struct { + id string + order int + apply func(e *gin.Engine) error +} + +func (m *mockOption) ID() string { + return m.id +} + +func (m *mockOption) Order() int { + return m.order +} + +func (m *mockOption) Apply(engine *gin.Engine) error { + if m.apply != nil { + return m.apply(engine) + } + return nil +} + +func TestApplyOptions(t *testing.T) { + gin.SetMode(gin.TestMode) + + testCases := []struct { + name string + options []Option + expectOrder []string + wantErr bool + }{ + { + name: "should apply in correct order", + options: []Option{ + &mockOption{id: "option-2", order: 2}, + &mockOption{id: "option-1", order: 1}, + &mockOption{id: "option-3", order: 3}, + }, + expectOrder: []string{"option-1", "option-2", "option-3"}, + wantErr: false, + }, + { + name: "should handle empty options", + options: []Option{}, + expectOrder: []string{}, + wantErr: false, + }, + { + name: "should return error on apply failure", + options: []Option{ + &mockOption{id: "good-option", order: 1}, + &mockOption{id: "bad-option", order: 2, apply: func(e *gin.Engine) error { + return errors.New("apply failed") + }}, + }, + expectOrder: []string{"good-option", "bad-option"}, // bad-option will not be used but it must be called once in the order. + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + engine := gin.New() + var appliedOrder []string + + // Wrap mock options to record apply calls + recordingOptions := make([]Option, len(tc.options)) + for i, opt := range tc.options { + mock, ok := opt.(*mockOption) + if !ok { + t.Fatalf("test setup error: expected mockOption") + } + // copy mock to avoid closure issues + originalApply := mock.apply + recordingOptions[i] = &mockOption{ + id: mock.id, + order: mock.order, + apply: func(e *gin.Engine) error { + appliedOrder = append(appliedOrder, mock.id) + if originalApply != nil { + return originalApply(e) + } + return nil + }, + } + } + + err := ApplyOptions(engine, recordingOptions) + + if (err != nil) != tc.wantErr { + t.Fatalf("ApplyOptions() error = %v, wantErr %v", err, tc.wantErr) + } + + if fmt.Sprint(appliedOrder) != fmt.Sprint(tc.expectOrder) { + t.Errorf("ApplyOptions() applied in wrong order. got=%v, want=%v", appliedOrder, tc.expectOrder) + } + }) + } +} + +func TestCorsOption(t *testing.T) { + gin.SetMode(gin.TestMode) + + config := cors.Config{ + AllowOrigins: []string{"http://localhost:4200"}, + } + + opt := CORS(config) + engine := gin.New() + + if err := opt.Apply(engine); err != nil { + t.Fatalf("Apply() failed: %v", err) + } + + // Check ID and Order + if opt.ID() != "cors" { + t.Errorf("ID() got = %q, want = \"cors\"", opt.ID()) + } + if opt.Order() != 1 { + t.Errorf("Order() got = %d, want = 1", opt.Order()) + } + + // Check if CORS header is present by making a request + engine.GET("/test", func(c *gin.Context) { + c.String(200, "ok") + }) + + req := httptest.NewRequest("GET", "/test", nil) + req.Header.Set("Origin", "http://localhost:4200") + w := httptest.NewRecorder() + engine.ServeHTTP(w, req) + + gotHeader := w.Header().Get("Access-Control-Allow-Origin") + if gotHeader != "http://localhost:4200" { + t.Errorf("Access-Control-Allow-Origin header not set correctly. got=%q, want=%q", gotHeader, "http://localhost:4200") + } +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 419e13fed..2cb66c176 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -33,7 +33,6 @@ import ( "github.com/GoogleCloudPlatform/khi/pkg/server/upload" inspectioncore_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/inspectioncore/contract" - "github.com/gin-contrib/cors" "github.com/gin-contrib/static" "github.com/gin-gonic/gin" ) @@ -56,18 +55,12 @@ func redirectMiddleware(exactPath string, redirectTo string) gin.HandlerFunc { } } -func CreateKHIServer(inspectionServer *coreinspection.InspectionTaskServer, serverConfig *ServerConfig) *gin.Engine { - engine := instanciateGinServer(parameters.Debug.Verbose != nil && *parameters.Debug.Verbose) - corsConfig := cors.DefaultConfig() - corsConfig.AllowAllOrigins = true - +func CreateKHIServer(engine *gin.Engine, inspectionServer *coreinspection.InspectionTaskServer, serverConfig *ServerConfig) *gin.Engine { appHtmlPath := path.Join(serverConfig.StaticFolderPath, "/index.html") basePathWithoutTrailingSlash := strings.TrimSuffix(serverConfig.ServerBasePath, "/") engine.Use(redirectMiddleware(basePathWithoutTrailingSlash+"/", basePathWithoutTrailingSlash+"/session/0")) // Request for `/` shouldn't be handled by `static.Serve`, redirect `/session/0` to be handled by patternToString engine.Use(static.Serve(basePathWithoutTrailingSlash+"/", static.LocalFile(serverConfig.StaticFolderPath, false))) - engine.Use(gin.Recovery()) - engine.Use(cors.New(corsConfig)) router := engine.Group(basePathWithoutTrailingSlash) // frontend uses Angular router. All frontend routing path should return the app html @@ -426,17 +419,3 @@ func CreateKHIServer(inspectionServer *coreinspection.InspectionTaskServer, serv } return engine } - -// instanciateGinServer generates a new instance of *gin.Engine with provided debug mode flag. -func instanciateGinServer(debugMode bool) *gin.Engine { - if debugMode { - gin.SetMode(gin.DebugMode) - } else { - gin.SetMode(gin.ReleaseMode) - } - engine := gin.New() - if debugMode { - engine.Use(gin.Logger()) - } - return engine -} diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index d2c419422..ce2df9334 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -34,6 +34,7 @@ import ( inspectiontaskbase "github.com/GoogleCloudPlatform/khi/pkg/core/inspection/taskbase" "github.com/GoogleCloudPlatform/khi/pkg/model/enum" "github.com/GoogleCloudPlatform/khi/pkg/parameters" + "github.com/gin-gonic/gin" "github.com/GoogleCloudPlatform/khi/pkg/core/task/taskid" "github.com/GoogleCloudPlatform/khi/pkg/server/config" @@ -247,7 +248,8 @@ func TestApiResponses(t *testing.T) { ResourceMonitor: &ResourceMonitorMock{UsedMemory: 1000}, ServerBasePath: "/foo", } - engine := CreateKHIServer(inspectionServer, &serverConfig) + engine := gin.New() + engine = CreateKHIServer(engine, inspectionServer, &serverConfig) // Perform requests with following oinvalidrder and verify if responses are matching with the expected values. scenarioSteps := []testScenarioStep{ @@ -788,7 +790,8 @@ func TestKHIServer_EndpointExistsWithConfigs(t *testing.T) { ResourceMonitor: &ResourceMonitorMock{UsedMemory: 1000}, ServerBasePath: tc.serverBasePath, } - engine := CreateKHIServer(inspectionServer, &config) + engine := gin.New() + engine = CreateKHIServer(engine, inspectionServer, &config) req, _ := http.NewRequest(tc.requestMethod, tc.requestPath, bytes.NewReader([]byte{})) engine.ServeHTTP(recorer, req) if recorer.Code != tc.wantCode { @@ -840,7 +843,8 @@ func TestKHIServerRedirects(t *testing.T) { ResourceMonitor: &ResourceMonitorMock{UsedMemory: 1000}, ServerBasePath: tc.serverBasePath, } - engine := CreateKHIServer(inspectionServer, &config) + engine := gin.New() + engine = CreateKHIServer(engine, inspectionServer, &config) req, _ := http.NewRequest(tc.requestMethod, tc.requestPath, bytes.NewReader([]byte{})) engine.ServeHTTP(recorer, req) if recorer.Code != tc.wantCode { @@ -914,7 +918,8 @@ func TestKHIDirectFileUpload(t *testing.T) { if err != nil { t.Fatalf("unexpected error %s", err) } - engine := CreateKHIServer(inspectionServer, &serverConfig) + engine := gin.New() + engine = CreateKHIServer(engine, inspectionServer, &serverConfig) parameters.Server.MaxUploadFileSizeInBytes = testutil.P(tc.maxUploadFileSize) var buf bytes.Buffer diff --git a/pkg/server/serverfactory.go b/pkg/server/serverfactory.go new file mode 100644 index 000000000..878ddf538 --- /dev/null +++ b/pkg/server/serverfactory.go @@ -0,0 +1,54 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "sync" + + "github.com/gin-gonic/gin" + + "github.com/GoogleCloudPlatform/khi/pkg/server/option" +) + +// DefaultServerFactory is the default instance of ServerFactory. +// This instance Options will be modified to extend the behavior of the gin server. +var DefaultServerFactory *ServerFactory = &ServerFactory{} + +// ServerFactory is responsible for creating and configuring Gin engine instances. +type ServerFactory struct { + Options []option.Option + mu sync.Mutex +} + +// AddOptions adds one or more Option instances to the factory's configuration. +func (s *ServerFactory) AddOptions(opt ...option.Option) { + s.mu.Lock() + defer s.mu.Unlock() + s.Options = append(s.Options, opt...) +} + +// CreateInstance creates a new Gin engine and applies all registered options to it. +// It returns the configured Gin engine or an error if any option fails to apply. +func (s *ServerFactory) CreateInstance(mode string) (*gin.Engine, error) { + s.mu.Lock() + defer s.mu.Unlock() + gin.SetMode(mode) + engine := gin.New() + err := option.ApplyOptions(engine, s.Options) + if err != nil { + return nil, err + } + return engine, nil +} diff --git a/pkg/server/serverfactory_test.go b/pkg/server/serverfactory_test.go new file mode 100644 index 000000000..ea611b48b --- /dev/null +++ b/pkg/server/serverfactory_test.go @@ -0,0 +1,130 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "errors" + "testing" + + "github.com/GoogleCloudPlatform/khi/pkg/server/option" + "github.com/gin-gonic/gin" +) + +// mockOption is a helper for testing. +type mockOption struct { + id string + order int + apply func(e *gin.Engine) error +} + +func (m *mockOption) ID() string { + return m.id +} + +func (m *mockOption) Order() int { + return m.order +} + +func (m *mockOption) Apply(engine *gin.Engine) error { + if m.apply != nil { + return m.apply(engine) + } + return nil +} + +func TestServerFactory_AddOptions(t *testing.T) { + factory := &ServerFactory{} + opt1 := &mockOption{id: "opt1"} + opt2 := &mockOption{id: "opt2"} + + factory.AddOptions(opt1) + if len(factory.Options) != 1 || factory.Options[0].ID() != "opt1" { + t.Errorf("AddOptions failed to add a single option. Got: %v", factory.Options) + } + + factory.AddOptions(opt2) + if len(factory.Options) != 2 || factory.Options[1].ID() != "opt2" { + t.Errorf("AddOptions failed to add a second option. Got: %v", factory.Options) + } +} + +func TestServerFactory_CreateInstance(t *testing.T) { + // Preserve original mode and restore after test + originalMode := gin.Mode() + defer gin.SetMode(originalMode) + + testCases := []struct { + name string + factory *ServerFactory + mode string + expectErr bool + expectOrder []string + }{ + { + name: "successful creation with ordered options", + factory: &ServerFactory{ + Options: []option.Option{ + &mockOption{id: "opt2", order: 2}, + &mockOption{id: "opt1", order: 1}, + }, + }, + mode: gin.TestMode, + expectErr: false, + }, + { + name: "creation fails when an option fails", + factory: &ServerFactory{ + Options: []option.Option{ + &mockOption{id: "good-opt", order: 1}, + &mockOption{id: "bad-opt", order: 2, apply: func(e *gin.Engine) error { + return errors.New("apply failed") + }}, + }, + }, + mode: gin.TestMode, + expectErr: true, + }, + { + name: "creation with no options", + factory: &ServerFactory{}, + mode: gin.DebugMode, + expectErr: false, + expectOrder: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + engine, err := tc.factory.CreateInstance(tc.mode) + + if (err != nil) != tc.expectErr { + t.Fatalf("CreateInstance() error = %v, wantErr %v", err, tc.expectErr) + } + + if tc.expectErr { + if engine != nil { + t.Error("CreateInstance() expected nil engine on error, but got one") + } + } else { + if engine == nil { + t.Fatal("CreateInstance() returned nil engine, but expected one") + } + if gin.Mode() != tc.mode { + t.Errorf("gin mode not set correctly. got=%q, want=%q", gin.Mode(), tc.mode) + } + } + }) + } +}