From 9e71ee70aac74b425d10cb88f28034708a4901e8 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 15:55:13 -0400 Subject: [PATCH 01/12] Make resolving utility functions type agnostic Make the utility functions used for resolving plugins by ID, URI, or Kubernetes reference not depend on the PluginComponent type and instead take the relevant information directly. This is required to allow reusing these functions for resolving Devfile parents Signed-off-by: Angel Misevski --- .../workspace/devworkspace_controller.go | 10 +-- pkg/library/flatten/flatten.go | 67 +++++++++++-------- .../error_invalid-schema-version.yaml | 2 +- .../testdata/plugin-id/error_on-fetch.yaml | 2 +- .../plugin-id/error_plugin-not-found.yaml | 2 +- .../plugin-id/error_unparseable-url.yaml | 2 +- .../plugin-id/resolve-plugin-by-id.yaml | 2 +- .../resolve-plugin-multiple-registries.yaml | 4 +- 8 files changed, 50 insertions(+), 41 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 8d4ef464f..3dc56a730 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -162,11 +162,11 @@ func (r *DevWorkspaceReconciler) Reconcile(req ctrl.Request) (reconcileResult ct timing.SetTime(timingInfo, timing.ComponentsCreated) // TODO#185 : Temporarily do devfile flattening in main reconcile loop; this should be moved to a subcontroller. flattenHelpers := flatten.ResolverTools{ - InstanceNamespace: workspace.Namespace, - Context: ctx, - K8sClient: r.Client, - InternalRegistry: ®istry.InternalRegistryImpl{}, - HttpClient: http.DefaultClient, + DefaultNamespace: workspace.Namespace, + Context: ctx, + K8sClient: r.Client, + InternalRegistry: ®istry.InternalRegistryImpl{}, + HttpClient: http.DefaultClient, } flattenedWorkspace, err := flatten.ResolveDevWorkspace(workspace.Spec.Template, flattenHelpers) if err != nil { diff --git a/pkg/library/flatten/flatten.go b/pkg/library/flatten/flatten.go index 091b05f27..308520ee1 100644 --- a/pkg/library/flatten/flatten.go +++ b/pkg/library/flatten/flatten.go @@ -30,11 +30,11 @@ import ( ) type ResolverTools struct { - InstanceNamespace string - Context context.Context - K8sClient client.Client - InternalRegistry registry.InternalRegistry - HttpClient network.HTTPGetter + DefaultNamespace string + Context context.Context + K8sClient client.Client + InternalRegistry registry.InternalRegistry + HttpClient network.HTTPGetter } // ResolveDevWorkspace takes a devworkspace and returns a "resolved" version of it -- i.e. one where all plugins and parents @@ -106,22 +106,23 @@ func recursiveResolve(workspace devworkspace.DevWorkspaceTemplateSpec, tooling R }, nil } +// resolvePluginComponent resolves the DevWorkspaceTemplateSpec that a plugin component refers to. The name parameter is +// used to construct meaningful error messages (e.g. issue resolving plugin 'name') func resolvePluginComponent( name string, plugin *devworkspace.PluginComponent, tooling ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { switch { - // TODO: Add support for plugin ID and URI case plugin.Kubernetes != nil: - // Search in devworkspace's namespace if namespace ref is unset + // Search in default namespace if namespace ref is unset if plugin.Kubernetes.Namespace == "" { - plugin.Kubernetes.Namespace = tooling.InstanceNamespace + plugin.Kubernetes.Namespace = tooling.DefaultNamespace } - resolvedPlugin, err = resolvePluginComponentByKubernetesReference(name, plugin, tooling) + resolvedPlugin, err = resolveElementByKubernetesImport(name, plugin.Kubernetes, tooling) case plugin.Uri != "": - resolvedPlugin, err = resolvePluginComponentByURI(name, plugin, tooling) + resolvedPlugin, err = resolveElementByURI(name, plugin.Uri, tooling) case plugin.Id != "": - resolvedPlugin, err = resolvePluginComponentById(name, plugin, tooling) + resolvedPlugin, err = resolveElementById(name, plugin.Id, plugin.RegistryUrl, tooling) default: err = fmt.Errorf("plugin %s does not define any resources", name) } @@ -143,15 +144,17 @@ func resolvePluginComponent( return resolvedPlugin, nil } -func resolvePluginComponentByKubernetesReference( +// resolveElementByKubernetesImport resolves a plugin specified by a Kubernetes reference. +// The name parameter is used to construct meaningful error messages (e.g. issue resolving plugin 'name') +func resolveElementByKubernetesImport( name string, - plugin *devworkspace.PluginComponent, + kubeReference *devworkspace.KubernetesCustomResourceImportReference, tooling ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { var dwTemplate devworkspace.DevWorkspaceTemplate namespacedName := types.NamespacedName{ - Name: plugin.Kubernetes.Name, - Namespace: plugin.Kubernetes.Namespace, + Name: kubeReference.Name, + Namespace: kubeReference.Namespace, } err = tooling.K8sClient.Get(tooling.Context, namespacedName, &dwTemplate) if err != nil { @@ -163,47 +166,53 @@ func resolvePluginComponentByKubernetesReference( return &dwTemplate.Spec, nil } -func resolvePluginComponentById( +// resolveElementById resolves a component specified by ID and registry URL. The name parameter is used to +// construct meaningful error messages (e.g. issue resolving plugin 'name'). When registry URL is empty, +// the DefaultRegistryURL from tools is used. +func resolveElementById( name string, - plugin *devworkspace.PluginComponent, + id string, + registryUrl string, tools ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { // Check internal registry for plugins that do not specify a registry - if plugin.RegistryUrl == "" { + if registryUrl == "" { if tools.InternalRegistry == nil { return nil, fmt.Errorf("plugin %s does not specify a registryUrl and no internal registry is configured", name) } - if !tools.InternalRegistry.IsInInternalRegistry(plugin.Id) { + if !tools.InternalRegistry.IsInInternalRegistry(id) { return nil, fmt.Errorf("plugin for component %s does not specify a registry and is not present in the internal registry", name) } - pluginDWT, err := tools.InternalRegistry.ReadPluginFromInternalRegistry(plugin.Id) + pluginDWT, err := tools.InternalRegistry.ReadPluginFromInternalRegistry(id) if err != nil { return nil, fmt.Errorf("failed to read plugin for component %s from internal registry: %w", name, err) } return &pluginDWT.Spec, nil - } - pluginURL, err := url.Parse(plugin.RegistryUrl) + } + pluginURL, err := url.Parse(registryUrl) if err != nil { - return nil, fmt.Errorf("failed to parse registry URL for plugin %s: %w", name, err) + return nil, fmt.Errorf("failed to parse registry URL for component %s: %w", name, err) } - pluginURL.Path = path.Join(pluginURL.Path, "plugins", plugin.Id) + pluginURL.Path = path.Join(pluginURL.Path, id) dwt, err := network.FetchDevWorkspaceTemplate(pluginURL.String(), tools.HttpClient) if err != nil { - return nil, fmt.Errorf("failed to resolve plugin %s from registry %s: %w", name, plugin.RegistryUrl, err) + return nil, fmt.Errorf("failed to resolve component %s from registry %s: %w", name, registryUrl, err) } return dwt, nil } -func resolvePluginComponentByURI( +// resolveElementByURI resolves a plugin defined by URI. The name parameter is used to construct meaningful +// error messages (e.g. issue resolving plugin 'name') +func resolveElementByURI( name string, - plugin *devworkspace.PluginComponent, + uri string, tools ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { - dwt, err := network.FetchDevWorkspaceTemplate(plugin.Uri, tools.HttpClient) + dwt, err := network.FetchDevWorkspaceTemplate(uri, tools.HttpClient) if err != nil { - return nil, fmt.Errorf("failed to resolve plugin %s by URI: %w", name, err) + return nil, fmt.Errorf("failed to resolve component %s by URI: %w", name, err) } return dwt, nil } diff --git a/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml b/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml index 9b2839acc..4eaff077f 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml @@ -8,7 +8,7 @@ input: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" devfilePlugins: - "https://test-registry.io/subpath/plugins/my/test/plugin": + "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 1.0.0 metadata: name: "plugin-a" diff --git a/pkg/library/flatten/testdata/plugin-id/error_on-fetch.yaml b/pkg/library/flatten/testdata/plugin-id/error_on-fetch.yaml index 92a44a691..6267d3501 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_on-fetch.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_on-fetch.yaml @@ -8,7 +8,7 @@ input: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" errors: - "https://test-registry.io/subpath/plugins/my/test/plugin": + "https://test-registry.io/subpath/my/test/plugin": message: "testing error" output: diff --git a/pkg/library/flatten/testdata/plugin-id/error_plugin-not-found.yaml b/pkg/library/flatten/testdata/plugin-id/error_plugin-not-found.yaml index 36da9f474..eccc35839 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_plugin-not-found.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_plugin-not-found.yaml @@ -8,7 +8,7 @@ input: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" errors: - "https://test-registry.io/subpath/plugins/my/test/plugin": + "https://test-registry.io/subpath/my/test/plugin": statusCode: 404 output: diff --git a/pkg/library/flatten/testdata/plugin-id/error_unparseable-url.yaml b/pkg/library/flatten/testdata/plugin-id/error_unparseable-url.yaml index 62fd534ac..4669ce023 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_unparseable-url.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_unparseable-url.yaml @@ -9,4 +9,4 @@ input: registryUrl: ":/test-registry.io/subpath" output: - errRegexp: "failed to parse registry URL for plugin test-plugin" + errRegexp: "failed to parse registry URL for component test-plugin" diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml index 3c2ed8075..a8018e2f8 100644 --- a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml +++ b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml @@ -8,7 +8,7 @@ input: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" devfilePlugins: - "https://test-registry.io/subpath/plugins/my/test/plugin": + "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 2.0.0 metadata: name: "plugin-a" diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml index 30201375d..d6e8a5279 100644 --- a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml +++ b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml @@ -12,7 +12,7 @@ input: id: my/test/plugin-2 registryUrl: "https://test-registry-2.io/subpath" devfilePlugins: - "https://test-registry.io/subpath/plugins/my/test/plugin": + "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 2.0.0 metadata: name: "plugin-a" @@ -21,7 +21,7 @@ input: container: name: test-container image: test-image - "https://test-registry-2.io/subpath/plugins/my/test/plugin-2": + "https://test-registry-2.io/subpath/my/test/plugin-2": schemaVersion: 2.0.0 metadata: name: "plugin-b" From 0dcc096fde7b63f9dc15b454286ddf995f7dcfc3 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 15:59:02 -0400 Subject: [PATCH 02/12] Add support for resolving devfile parents in same way as plugins Add support for resolving devfile parents in same way we resolve plugins (supporting id, URI, and kubernetes reference) Signed-off-by: Angel Misevski --- .../workspace/devworkspace_controller.go | 4 +- pkg/library/container/container.go | 2 +- pkg/library/flatten/common.go | 2 +- pkg/library/flatten/flatten.go | 57 +++++++++++++++---- .../flatten/internal/testutil/common.go | 2 +- .../testdata/k8s-ref/error_has-parent.yaml | 15 ----- 6 files changed, 51 insertions(+), 31 deletions(-) delete mode 100644 pkg/library/flatten/testdata/k8s-ref/error_has-parent.yaml diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 3dc56a730..357441014 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -168,7 +168,7 @@ func (r *DevWorkspaceReconciler) Reconcile(req ctrl.Request) (reconcileResult ct InternalRegistry: ®istry.InternalRegistryImpl{}, HttpClient: http.DefaultClient, } - flattenedWorkspace, err := flatten.ResolveDevWorkspace(workspace.Spec.Template, flattenHelpers) + flattenedWorkspace, err := flatten.ResolveDevWorkspace(&workspace.Spec.Template, flattenHelpers) if err != nil { reqLogger.Info("DevWorkspace start failed") reconcileStatus.Phase = devworkspace.WorkspaceStatusFailed @@ -186,7 +186,7 @@ func (r *DevWorkspaceReconciler) Reconcile(req ctrl.Request) (reconcileResult ct } } - devfilePodAdditions, err := containerlib.GetKubeContainersFromDevfile(workspace.Spec.Template) + devfilePodAdditions, err := containerlib.GetKubeContainersFromDevfile(&workspace.Spec.Template) if err != nil { reqLogger.Info("DevWorkspace start failed") reconcileStatus.Phase = devworkspace.WorkspaceStatusFailed diff --git a/pkg/library/container/container.go b/pkg/library/container/container.go index 74f79c6cf..14051a7f4 100644 --- a/pkg/library/container/container.go +++ b/pkg/library/container/container.go @@ -40,7 +40,7 @@ import ( // rewritten as Volumes are added to PodAdditions, in order to support e.g. using one PVC to hold all volumes // // Note: Requires DevWorkspace to be flattened (i.e. the DevWorkspace contains no Parent or Components of type Plugin) -func GetKubeContainersFromDevfile(workspace devworkspace.DevWorkspaceTemplateSpec) (*v1alpha1.PodAdditions, error) { +func GetKubeContainersFromDevfile(workspace *devworkspace.DevWorkspaceTemplateSpec) (*v1alpha1.PodAdditions, error) { if !flatten.DevWorkspaceIsFlattened(workspace) { return nil, fmt.Errorf("devfile is not flattened") } diff --git a/pkg/library/flatten/common.go b/pkg/library/flatten/common.go index f86e04206..7c014700d 100644 --- a/pkg/library/flatten/common.go +++ b/pkg/library/flatten/common.go @@ -14,7 +14,7 @@ package flatten import devworkspace "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" -func DevWorkspaceIsFlattened(devworkspace devworkspace.DevWorkspaceTemplateSpec) bool { +func DevWorkspaceIsFlattened(devworkspace *devworkspace.DevWorkspaceTemplateSpec) bool { if devworkspace.Parent != nil { return false } diff --git a/pkg/library/flatten/flatten.go b/pkg/library/flatten/flatten.go index 308520ee1..1132baf85 100644 --- a/pkg/library/flatten/flatten.go +++ b/pkg/library/flatten/flatten.go @@ -39,12 +39,9 @@ type ResolverTools struct { // ResolveDevWorkspace takes a devworkspace and returns a "resolved" version of it -- i.e. one where all plugins and parents // are inlined as components. -// TODO: -// - Implement flattening for DevWorkspace parents -// - Implement plugin references by ID and URI -func ResolveDevWorkspace(workspace devworkspace.DevWorkspaceTemplateSpec, tooling ResolverTools) (*devworkspace.DevWorkspaceTemplateSpec, error) { +func ResolveDevWorkspace(workspace *devworkspace.DevWorkspaceTemplateSpec, tooling ResolverTools) (*devworkspace.DevWorkspaceTemplateSpec, error) { // Web terminals get default container components if they do not specify one - if err := web_terminal.AddDefaultContainerIfNeeded(&workspace); err != nil { + if err := web_terminal.AddDefaultContainerIfNeeded(workspace); err != nil { return nil, err } @@ -56,15 +53,23 @@ func ResolveDevWorkspace(workspace devworkspace.DevWorkspaceTemplateSpec, toolin return resolvedDW, nil } -func recursiveResolve(workspace devworkspace.DevWorkspaceTemplateSpec, tooling ResolverTools, resolveCtx *resolutionContextTree) (*devworkspace.DevWorkspaceTemplateSpec, error) { +func recursiveResolve(workspace *devworkspace.DevWorkspaceTemplateSpec, tooling ResolverTools, resolveCtx *resolutionContextTree) (*devworkspace.DevWorkspaceTemplateSpec, error) { if DevWorkspaceIsFlattened(workspace) { return workspace.DeepCopy(), nil } + + resolvedParent := &devworkspace.DevWorkspaceTemplateSpecContent{} if workspace.Parent != nil { - // TODO: Add support for flattening DevWorkspace parents - return nil, fmt.Errorf("DevWorkspace parent is unsupported") + resolvedParentSpec, err := resolveParentComponent(workspace.Parent, tooling) + if err != nil { + return nil, err + } + if !DevWorkspaceIsFlattened(resolvedParentSpec) { + // TODO: implemenent this + return nil, fmt.Errorf("parents containing plugins or parents are not supported") + } + resolvedParent = &resolvedParentSpec.DevWorkspaceTemplateSpecContent } - resolvedContent := &devworkspace.DevWorkspaceTemplateSpecContent{} resolvedContent.Projects = workspace.Projects resolvedContent.StarterProjects = workspace.StarterProjects @@ -86,7 +91,7 @@ func recursiveResolve(workspace devworkspace.DevWorkspaceTemplateSpec, tooling R return nil, err } - resolvedPlugin, err := recursiveResolve(*pluginComponent, tooling, newCtx) + resolvedPlugin, err := recursiveResolve(pluginComponent, tooling, newCtx) if err != nil { return nil, err } @@ -96,7 +101,7 @@ func recursiveResolve(workspace devworkspace.DevWorkspaceTemplateSpec, tooling R } } - resolvedContent, err := overriding.MergeDevWorkspaceTemplateSpec(resolvedContent, nil, pluginSpecContents...) + resolvedContent, err := overriding.MergeDevWorkspaceTemplateSpec(resolvedContent, resolvedParent, pluginSpecContents...) if err != nil { return nil, fmt.Errorf("failed to merge DevWorkspace parents/plugins: %w", err) } @@ -106,6 +111,36 @@ func recursiveResolve(workspace devworkspace.DevWorkspaceTemplateSpec, tooling R }, nil } +// resolveParentComponent resolves the parent DevWorkspaceTemplateSpec that a parent reference refers to. +func resolveParentComponent(parent *devworkspace.Parent, tooling ResolverTools) (resolvedParent *devworkspace.DevWorkspaceTemplateSpec, err error) { + switch { + case parent.Kubernetes != nil: + // Search in default namespace if namespace ref is unset + if parent.Kubernetes.Namespace == "" { + parent.Kubernetes.Namespace = tooling.DefaultNamespace + } + resolvedParent, err = resolveElementByKubernetesImport("parent", parent.Kubernetes, tooling) + case parent.Uri != "": + resolvedParent, err = resolveElementByURI("parent", parent.Uri, tooling) + case parent.Id != "": + resolvedParent, err = resolveElementById("parent", parent.Id, parent.RegistryUrl, tooling) + default: + err = fmt.Errorf("devfile parent does not define any resources") + } + if err != nil { + return nil, err + } + if parent.Components != nil || parent.Commands != nil || parent.Projects != nil || parent.StarterProjects != nil { + overrideSpec, err := overriding.OverrideDevWorkspaceTemplateSpec(&resolvedParent.DevWorkspaceTemplateSpecContent, parent.ParentOverrides) + + if err != nil { + return nil, err + } + resolvedParent.DevWorkspaceTemplateSpecContent = *overrideSpec + } + return resolvedParent, nil +} + // resolvePluginComponent resolves the DevWorkspaceTemplateSpec that a plugin component refers to. The name parameter is // used to construct meaningful error messages (e.g. issue resolving plugin 'name') func resolvePluginComponent( diff --git a/pkg/library/flatten/internal/testutil/common.go b/pkg/library/flatten/internal/testutil/common.go index 19ad7084f..ea9d0e632 100644 --- a/pkg/library/flatten/internal/testutil/common.go +++ b/pkg/library/flatten/internal/testutil/common.go @@ -60,7 +60,7 @@ type TestCase struct { } type TestInput struct { - Workspace dw.DevWorkspaceTemplateSpec `json:"workspace,omitempty"` + Workspace *dw.DevWorkspaceTemplateSpec `json:"workspace,omitempty"` // Plugins is a map of plugin "name" to devworkspace template; namespace is ignored. Plugins map[string]dw.DevWorkspaceTemplate `json:"plugins,omitempty"` // DevfilePlugins is a map of plugin "name" to devfile diff --git a/pkg/library/flatten/testdata/k8s-ref/error_has-parent.yaml b/pkg/library/flatten/testdata/k8s-ref/error_has-parent.yaml deleted file mode 100644 index 36031cee0..000000000 --- a/pkg/library/flatten/testdata/k8s-ref/error_has-parent.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: "Workspace has parent" - -input: - workspace: - parent: - kubernetes: - name: my-parent - components: - - name: my-component - container: - image: test-image - - -output: - errRegexp: "DevWorkspace parent is unsupported" From df2dfa5f17773d5c23fa9393f5d5303f360b848d Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:02:52 -0400 Subject: [PATCH 03/12] Use devfile/api Devfile struct rather than our own Signed-off-by: Angel Misevski --- pkg/library/flatten/internal/testutil/common.go | 6 +++--- pkg/library/flatten/internal/testutil/http.go | 3 ++- pkg/library/flatten/network/devfile.go | 8 +------- pkg/library/flatten/network/fetch.go | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pkg/library/flatten/internal/testutil/common.go b/pkg/library/flatten/internal/testutil/common.go index ea9d0e632..7762af4dc 100644 --- a/pkg/library/flatten/internal/testutil/common.go +++ b/pkg/library/flatten/internal/testutil/common.go @@ -19,12 +19,12 @@ import ( "testing" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/devfile/devworkspace-operator/pkg/config" - "github.com/devfile/devworkspace-operator/pkg/library/flatten/network" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/yaml" + + "github.com/devfile/devworkspace-operator/pkg/config" ) var WorkspaceTemplateDiffOpts = cmp.Options{ @@ -64,7 +64,7 @@ type TestInput struct { // Plugins is a map of plugin "name" to devworkspace template; namespace is ignored. Plugins map[string]dw.DevWorkspaceTemplate `json:"plugins,omitempty"` // DevfilePlugins is a map of plugin "name" to devfile - DevfilePlugins map[string]network.Devfile `json:"devfilePlugins,omitempty"` + DevfilePlugins map[string]dw.Devfile `json:"devfilePlugins,omitempty"` // Errors is a map of plugin name to the error that should be returned when attempting to retrieve it. Errors map[string]TestPluginError `json:"errors,omitempty"` } diff --git a/pkg/library/flatten/internal/testutil/http.go b/pkg/library/flatten/internal/testutil/http.go index e0b2dfefb..158ecc940 100644 --- a/pkg/library/flatten/internal/testutil/http.go +++ b/pkg/library/flatten/internal/testutil/http.go @@ -19,13 +19,14 @@ import ( "io" "net/http" + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "sigs.k8s.io/yaml" "github.com/devfile/devworkspace-operator/pkg/library/flatten/network" ) type FakeHTTPGetter struct { - Plugins map[string]network.Devfile + Plugins map[string]dw.Devfile Errors map[string]TestPluginError } diff --git a/pkg/library/flatten/network/devfile.go b/pkg/library/flatten/network/devfile.go index 044eb5051..c866fb56b 100644 --- a/pkg/library/flatten/network/devfile.go +++ b/pkg/library/flatten/network/devfile.go @@ -17,17 +17,11 @@ import ( "regexp" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - devfilev2 "github.com/devfile/api/v2/pkg/devfile" ) var SupportedSchemaVersionRegexp = regexp.MustCompile(`^2\..+`) -type Devfile struct { - devfilev2.DevfileHeader - dw.DevWorkspaceTemplateSpec -} - -func ConvertDevfileToDevWorkspaceTemplate(devfile *Devfile) (*dw.DevWorkspaceTemplate, error) { +func ConvertDevfileToDevWorkspaceTemplate(devfile *dw.Devfile) (*dw.DevWorkspaceTemplate, error) { if !SupportedSchemaVersionRegexp.MatchString(devfile.SchemaVersion) { return nil, fmt.Errorf("could not process devfile: unsupported schemaVersion '%s'", devfile.SchemaVersion) } diff --git a/pkg/library/flatten/network/fetch.go b/pkg/library/flatten/network/fetch.go index f07d1344b..682ccc441 100644 --- a/pkg/library/flatten/network/fetch.go +++ b/pkg/library/flatten/network/fetch.go @@ -40,7 +40,7 @@ func FetchDevWorkspaceTemplate(location string, httpClient HTTPGetter) (*dw.DevW } // Assume we're getting a devfile, not a DevWorkspaceTemplate (TODO: Detect type and handle both?) - devfile := &Devfile{} + devfile := &dw.Devfile{} err = yaml.Unmarshal(bytes, devfile) if err != nil { return nil, fmt.Errorf("could not unmarshal devfile from response: %w", err) From 862df672c8a4d8f05042f8cc550bc72a27f45597 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:04:05 -0400 Subject: [PATCH 04/12] Add support for retreiving network resources as DevWorkspaces If attempting to unmarshal a network response to a devfile results in a struct without a schemaVersion, attempt to unmarshal to a DevWorkspace or DevWorkspaceTemplate instead. Signed-off-by: Angel Misevski --- pkg/library/flatten/network/fetch.go | 32 ++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/library/flatten/network/fetch.go b/pkg/library/flatten/network/fetch.go index 682ccc441..ae21a9a6b 100644 --- a/pkg/library/flatten/network/fetch.go +++ b/pkg/library/flatten/network/fetch.go @@ -39,17 +39,35 @@ func FetchDevWorkspaceTemplate(location string, httpClient HTTPGetter) (*dw.DevW return nil, fmt.Errorf("could not read data from %s: %w", location, err) } - // Assume we're getting a devfile, not a DevWorkspaceTemplate (TODO: Detect type and handle both?) devfile := &dw.Devfile{} - err = yaml.Unmarshal(bytes, devfile) - if err != nil { + if err := yaml.Unmarshal(bytes, devfile); err != nil { return nil, fmt.Errorf("could not unmarshal devfile from response: %w", err) } + if devfile.SchemaVersion != "" { + dwt, err := ConvertDevfileToDevWorkspaceTemplate(devfile) + if err != nil { + return nil, fmt.Errorf("failed to convert devfile to DevWorkspaceTemplate: %s", err) + } + return &dwt.Spec, nil + } - dwt, err := ConvertDevfileToDevWorkspaceTemplate(devfile) - if err != nil { - return nil, fmt.Errorf("failed to convert devfile to DevWorkspaceTemplate: %s", err) + // Assume we didn't get a devfile, check if content is DevWorkspace + devworkspace := &dw.DevWorkspace{} + if err := yaml.Unmarshal(bytes, devworkspace); err != nil { + return nil, fmt.Errorf("could not unmarshal devworkspace from response: %w", err) + } + if devworkspace.Kind == "DevWorkspace" { + return &devworkspace.Spec.Template, nil + } + + // Check if content is DevWorkspaceTemplate + dwt := &dw.DevWorkspaceTemplate{} + if err := yaml.Unmarshal(bytes, dwt); err != nil { + return nil, fmt.Errorf("could not unmarshal devworkspacetemplate from response: %w", err) + } + if dwt.Kind == "DevWorkspaceTemplate" { + return &dwt.Spec, nil } - return &dwt.Spec, nil + return nil, fmt.Errorf("could not find devfile or devworkspace object at '%s'", location) } From f5bfded76b522045f10a7aae00bb76fc990f1316 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:08:45 -0400 Subject: [PATCH 05/12] Add test cases to cover devworkspace(templates) on URIs Signed-off-by: Angel Misevski --- pkg/library/flatten/flatten_test.go | 10 +++--- pkg/library/flatten/internal/testutil/http.go | 19 +++++++++-- .../error_fetch-unparseable-file.yaml | 23 +++++++++++++ ...solve-devworkspace-instead-of-devfile.yaml | 32 +++++++++++++++++++ .../error_fetch-unparseable-file.yaml | 22 +++++++++++++ ...solve-devworkspace-instead-of-devfile.yaml | 31 ++++++++++++++++++ 6 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml create mode 100644 pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml create mode 100644 pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml create mode 100644 pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml diff --git a/pkg/library/flatten/flatten_test.go b/pkg/library/flatten/flatten_test.go index 64ad8b371..eadafa0dd 100644 --- a/pkg/library/flatten/flatten_test.go +++ b/pkg/library/flatten/flatten_test.go @@ -89,8 +89,9 @@ func TestResolveDevWorkspacePluginRegistry(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testHttpGetter := &testutil.FakeHTTPGetter{ - Plugins: tt.Input.DevfilePlugins, - Errors: tt.Input.Errors, + DevfilePlugins: tt.Input.DevfilePlugins, + DevWorkspacePlugins: tt.Input.Plugins, + Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ Context: context.Background(), @@ -119,8 +120,9 @@ func TestResolveDevWorkspacePluginURI(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testHttpGetter := &testutil.FakeHTTPGetter{ - Plugins: tt.Input.DevfilePlugins, - Errors: tt.Input.Errors, + DevfilePlugins: tt.Input.DevfilePlugins, + DevWorkspacePlugins: tt.Input.Plugins, + Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ Context: context.Background(), diff --git a/pkg/library/flatten/internal/testutil/http.go b/pkg/library/flatten/internal/testutil/http.go index 158ecc940..49ff81aa8 100644 --- a/pkg/library/flatten/internal/testutil/http.go +++ b/pkg/library/flatten/internal/testutil/http.go @@ -26,8 +26,9 @@ import ( ) type FakeHTTPGetter struct { - Plugins map[string]dw.Devfile - Errors map[string]TestPluginError + DevfilePlugins map[string]dw.Devfile + DevWorkspacePlugins map[string]dw.DevWorkspaceTemplate + Errors map[string]TestPluginError } var _ network.HTTPGetter = (*FakeHTTPGetter)(nil) @@ -39,7 +40,7 @@ type fakeRespBody struct { func (_ *fakeRespBody) Close() error { return nil } func (reg *FakeHTTPGetter) Get(location string) (*http.Response, error) { - if plugin, ok := reg.Plugins[location]; ok { + if plugin, ok := reg.DevfilePlugins[location]; ok { yamlBytes, err := yaml.Marshal(plugin) if err != nil { return nil, fmt.Errorf("error marshalling plugin in test: %w", err) @@ -50,6 +51,18 @@ func (reg *FakeHTTPGetter) Get(location string) (*http.Response, error) { } return resp, nil } + if plugin, ok := reg.DevWorkspacePlugins[location]; ok { + yamlBytes, err := yaml.Marshal(plugin) + if err != nil { + return nil, fmt.Errorf("error marshalling plugin in test: %w", err) + } + resp := &http.Response{ + StatusCode: http.StatusOK, + Body: &fakeRespBody{bytes.NewBuffer(yamlBytes)}, + } + return resp, nil + } + if err, ok := reg.Errors[location]; ok { if err.StatusCode != 0 { return &http.Response{ diff --git a/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml b/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml new file mode 100644 index 000000000..570f2d1f9 --- /dev/null +++ b/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml @@ -0,0 +1,23 @@ +name: "DevWorkspace registry contains non-devfile type content" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + plugins: + "https://test-registry.io/subpath/my/test/plugin": + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + errRegexp: "could not find devfile or devworkspace object at 'https://test-registry.io/subpath/my/test/plugin'" diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml new file mode 100644 index 000000000..9ee833e80 --- /dev/null +++ b/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml @@ -0,0 +1,32 @@ +name: "DevWorkspace references DevWorkspaceTemplate plugin from registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + plugins: + "https://test-registry.io/subpath/my/test/plugin": + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + workspace: + components: + - name: plugin-a + attributes: + controller.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image diff --git a/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml b/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml new file mode 100644 index 000000000..2fc440542 --- /dev/null +++ b/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml @@ -0,0 +1,22 @@ +name: "DevWorkspace reference URI containing non-devfile type content" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + plugins: + "https://my-plugin.io/test": + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + errRegexp: "could not find devfile or devworkspace object at 'https://my-plugin.io/test'" diff --git a/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml b/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml new file mode 100644 index 000000000..0c3bb6f23 --- /dev/null +++ b/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml @@ -0,0 +1,31 @@ +name: "DevWorkspace references DevWorkspaceTemplate plugin from registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + plugins: + "https://my-plugin.io/test": + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + workspace: + components: + - name: plugin-a + attributes: + controller.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image From cf486d76a688342ceeabf3ea0e849af4f7b97221 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:26:44 -0400 Subject: [PATCH 06/12] Modify test struct names to accomodate parent tests Rename plugin-related struct fields in tests to be generic, to allow them to hold any resources referenced. Required for reusing these structs cleanly in parent tests. Signed-off-by: Angel Misevski --- pkg/library/flatten/flatten_test.go | 53 ++++++++++++--- .../flatten/internal/testutil/common.go | 8 +-- pkg/library/flatten/internal/testutil/http.go | 10 +-- .../flatten/internal/testutil/k8sClient.go | 8 +-- .../defaulting-web-terminal-component.yaml | 2 +- .../plugin-in-internal-registry.yaml | 2 +- .../k8s-ref/error_bad-plugin-merge.yaml | 2 +- .../k8s-ref/error_conflicting-merge.yaml | 2 +- .../k8s-ref/error_plugin-references-self.yml | 2 +- .../k8s-ref/error_plugins-have-cycle.yml | 2 +- .../k8s-ref/nested-plugins-annotation.yaml | 2 +- .../testdata/k8s-ref/nodejs-workspace.yaml | 2 +- .../k8s-ref/web-terminal-with-plugin.yaml | 2 +- .../parent/error_parent-has-parent.yaml | 31 +++++++++ .../parent/error_parent-has-plugins.yaml | 26 +++++++ .../parent/resolve-parent-and-plugins.yaml | 68 +++++++++++++++++++ .../testdata/parent/resolve-parent-by-id.yaml | 44 ++++++++++++ .../resolve-parent-by-k8s-reference.yaml | 46 +++++++++++++ .../parent/resolve-parent-by-uri.yaml | 43 ++++++++++++ .../error_fetch-unparseable-file.yaml | 2 +- .../error_invalid-schema-version.yaml | 2 +- ...solve-devworkspace-instead-of-devfile.yaml | 2 +- .../plugin-id/resolve-plugin-by-id.yaml | 2 +- .../resolve-plugin-multiple-registries.yaml | 2 +- .../error_fetch-unparseable-file.yaml | 2 +- .../error_invalid-schema-version.yaml | 2 +- ...solve-devworkspace-instead-of-devfile.yaml | 2 +- .../resolve-multiple-plugins-by-uri.yaml | 2 +- .../plugin-uri/resolve-plugin-by-uri.yaml | 2 +- 29 files changed, 334 insertions(+), 41 deletions(-) create mode 100644 pkg/library/flatten/testdata/parent/error_parent-has-parent.yaml create mode 100644 pkg/library/flatten/testdata/parent/error_parent-has-plugins.yaml create mode 100644 pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml create mode 100644 pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml create mode 100644 pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml create mode 100644 pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml diff --git a/pkg/library/flatten/flatten_test.go b/pkg/library/flatten/flatten_test.go index eadafa0dd..adb89f035 100644 --- a/pkg/library/flatten/flatten_test.go +++ b/pkg/library/flatten/flatten_test.go @@ -29,8 +29,8 @@ func TestResolveDevWorkspaceKubernetesReference(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testClient := &testutil.FakeK8sClient{ - Plugins: tt.Input.Plugins, - Errors: tt.Input.Errors, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ Context: context.Background(), @@ -59,7 +59,7 @@ func TestResolveDevWorkspaceInternalRegistry(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testRegistry := &testutil.FakeInternalRegistry{ - Plugins: tt.Input.Plugins, + Plugins: tt.Input.DevWorkspaceResources, Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ @@ -89,9 +89,9 @@ func TestResolveDevWorkspacePluginRegistry(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testHttpGetter := &testutil.FakeHTTPGetter{ - DevfilePlugins: tt.Input.DevfilePlugins, - DevWorkspacePlugins: tt.Input.Plugins, - Errors: tt.Input.Errors, + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ Context: context.Background(), @@ -120,12 +120,47 @@ func TestResolveDevWorkspacePluginURI(t *testing.T) { // sanity check: input defines components assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") testHttpGetter := &testutil.FakeHTTPGetter{ - DevfilePlugins: tt.Input.DevfilePlugins, - DevWorkspacePlugins: tt.Input.Plugins, - Errors: tt.Input.Errors, + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + HttpClient: testHttpGetter, + } + outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspaceParents(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/parent") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ Context: context.Background(), + K8sClient: testK8sClient, HttpClient: testHttpGetter, } outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) diff --git a/pkg/library/flatten/internal/testutil/common.go b/pkg/library/flatten/internal/testutil/common.go index 7762af4dc..2ea1837fc 100644 --- a/pkg/library/flatten/internal/testutil/common.go +++ b/pkg/library/flatten/internal/testutil/common.go @@ -61,10 +61,10 @@ type TestCase struct { type TestInput struct { Workspace *dw.DevWorkspaceTemplateSpec `json:"workspace,omitempty"` - // Plugins is a map of plugin "name" to devworkspace template; namespace is ignored. - Plugins map[string]dw.DevWorkspaceTemplate `json:"plugins,omitempty"` - // DevfilePlugins is a map of plugin "name" to devfile - DevfilePlugins map[string]dw.Devfile `json:"devfilePlugins,omitempty"` + // DevWorkspaceResources is a map of string keys to devworkspace templates + DevWorkspaceResources map[string]dw.DevWorkspaceTemplate `json:"devworkspaceResources,omitempty"` + // DevfileResources is a map of string keys to devfile resources + DevfileResources map[string]dw.Devfile `json:"devfileResources,omitempty"` // Errors is a map of plugin name to the error that should be returned when attempting to retrieve it. Errors map[string]TestPluginError `json:"errors,omitempty"` } diff --git a/pkg/library/flatten/internal/testutil/http.go b/pkg/library/flatten/internal/testutil/http.go index 49ff81aa8..0a5666def 100644 --- a/pkg/library/flatten/internal/testutil/http.go +++ b/pkg/library/flatten/internal/testutil/http.go @@ -26,9 +26,9 @@ import ( ) type FakeHTTPGetter struct { - DevfilePlugins map[string]dw.Devfile - DevWorkspacePlugins map[string]dw.DevWorkspaceTemplate - Errors map[string]TestPluginError + DevfileResources map[string]dw.Devfile + DevWorkspaceResources map[string]dw.DevWorkspaceTemplate + Errors map[string]TestPluginError } var _ network.HTTPGetter = (*FakeHTTPGetter)(nil) @@ -40,7 +40,7 @@ type fakeRespBody struct { func (_ *fakeRespBody) Close() error { return nil } func (reg *FakeHTTPGetter) Get(location string) (*http.Response, error) { - if plugin, ok := reg.DevfilePlugins[location]; ok { + if plugin, ok := reg.DevfileResources[location]; ok { yamlBytes, err := yaml.Marshal(plugin) if err != nil { return nil, fmt.Errorf("error marshalling plugin in test: %w", err) @@ -51,7 +51,7 @@ func (reg *FakeHTTPGetter) Get(location string) (*http.Response, error) { } return resp, nil } - if plugin, ok := reg.DevWorkspacePlugins[location]; ok { + if plugin, ok := reg.DevWorkspaceResources[location]; ok { yamlBytes, err := yaml.Marshal(plugin) if err != nil { return nil, fmt.Errorf("error marshalling plugin in test: %w", err) diff --git a/pkg/library/flatten/internal/testutil/k8sClient.go b/pkg/library/flatten/internal/testutil/k8sClient.go index 1694c7551..1acab810f 100644 --- a/pkg/library/flatten/internal/testutil/k8sClient.go +++ b/pkg/library/flatten/internal/testutil/k8sClient.go @@ -25,9 +25,9 @@ import ( ) type FakeK8sClient struct { - client.Client // To satisfy interface; override all used methods - Plugins map[string]v1alpha2.DevWorkspaceTemplate - Errors map[string]TestPluginError + client.Client // To satisfy interface; override all used methods + DevWorkspaceResources map[string]v1alpha2.DevWorkspaceTemplate + Errors map[string]TestPluginError } func (client *FakeK8sClient) Get(_ context.Context, namespacedName client.ObjectKey, obj runtime.Object) error { @@ -35,7 +35,7 @@ func (client *FakeK8sClient) Get(_ context.Context, namespacedName client.Object if !ok { return fmt.Errorf("called Get() in fake client with non-DevWorkspaceTemplate") } - if plugin, ok := client.Plugins[namespacedName.Name]; ok { + if plugin, ok := client.DevWorkspaceResources[namespacedName.Name]; ok { *template = plugin return nil } diff --git a/pkg/library/flatten/testdata/internal-registry/defaulting-web-terminal-component.yaml b/pkg/library/flatten/testdata/internal-registry/defaulting-web-terminal-component.yaml index e3589d647..e50740e07 100644 --- a/pkg/library/flatten/testdata/internal-registry/defaulting-web-terminal-component.yaml +++ b/pkg/library/flatten/testdata/internal-registry/defaulting-web-terminal-component.yaml @@ -7,7 +7,7 @@ input: plugin: id: redhat-developer/web-terminal/4.5.0 - plugins: + devworkspaceResources: redhat-developer/web-terminal/4.5.0: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/internal-registry/plugin-in-internal-registry.yaml b/pkg/library/flatten/testdata/internal-registry/plugin-in-internal-registry.yaml index a4dfd50ff..e56982be2 100644 --- a/pkg/library/flatten/testdata/internal-registry/plugin-in-internal-registry.yaml +++ b/pkg/library/flatten/testdata/internal-registry/plugin-in-internal-registry.yaml @@ -6,7 +6,7 @@ input: - name: test-plugin plugin: id: my/test/plugin - plugins: + devworkspaceResources: my/test/plugin: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml b/pkg/library/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml index 97df12ef4..f5fdb20c9 100644 --- a/pkg/library/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml +++ b/pkg/library/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml @@ -11,7 +11,7 @@ input: - name: non-existent container: memoryLimit: 512Mi - plugins: + devworkspaceResources: override: spec: components: diff --git a/pkg/library/flatten/testdata/k8s-ref/error_conflicting-merge.yaml b/pkg/library/flatten/testdata/k8s-ref/error_conflicting-merge.yaml index a41d0ea09..0c096bca6 100644 --- a/pkg/library/flatten/testdata/k8s-ref/error_conflicting-merge.yaml +++ b/pkg/library/flatten/testdata/k8s-ref/error_conflicting-merge.yaml @@ -10,7 +10,7 @@ input: - name: my-component container: image: test-image - plugins: + devworkspaceResources: test-plugin: spec: components: diff --git a/pkg/library/flatten/testdata/k8s-ref/error_plugin-references-self.yml b/pkg/library/flatten/testdata/k8s-ref/error_plugin-references-self.yml index c551e576e..61084f171 100644 --- a/pkg/library/flatten/testdata/k8s-ref/error_plugin-references-self.yml +++ b/pkg/library/flatten/testdata/k8s-ref/error_plugin-references-self.yml @@ -7,7 +7,7 @@ input: plugin: kubernetes: name: plugin-a - plugins: + devworkspaceResources: plugin-a: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml b/pkg/library/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml index 9a8f8ee08..f6fcfff14 100644 --- a/pkg/library/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml +++ b/pkg/library/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml @@ -7,7 +7,7 @@ input: plugin: kubernetes: name: plugin-a - plugins: + devworkspaceResources: plugin-a: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml b/pkg/library/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml index de7dde79f..c61dc9c50 100644 --- a/pkg/library/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml +++ b/pkg/library/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml @@ -8,7 +8,7 @@ input: kubernetes: name: test-plugin-a namespace: test-ns - plugins: + devworkspaceResources: test-plugin-a: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/k8s-ref/nodejs-workspace.yaml b/pkg/library/flatten/testdata/k8s-ref/nodejs-workspace.yaml index 44a413f27..2351c7c9c 100644 --- a/pkg/library/flatten/testdata/k8s-ref/nodejs-workspace.yaml +++ b/pkg/library/flatten/testdata/k8s-ref/nodejs-workspace.yaml @@ -76,7 +76,7 @@ input: } ] } - plugins: + devworkspaceResources: theia-next: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml b/pkg/library/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml index c598788db..8f32c78ba 100644 --- a/pkg/library/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml +++ b/pkg/library/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml @@ -16,7 +16,7 @@ input: kubernetes: name: web-terminal namespace: devworkspace-plugins - plugins: + devworkspaceResources: web-terminal: kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/parent/error_parent-has-parent.yaml b/pkg/library/flatten/testdata/parent/error_parent-has-parent.yaml new file mode 100644 index 000000000..4de009da0 --- /dev/null +++ b/pkg/library/flatten/testdata/parent/error_parent-has-parent.yaml @@ -0,0 +1,31 @@ +name: "Fails when parent is not flattened (has parent)" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + parent: + id: another-parent + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + errRegexp: "parents containing plugins or parents are not supported" diff --git a/pkg/library/flatten/testdata/parent/error_parent-has-plugins.yaml b/pkg/library/flatten/testdata/parent/error_parent-has-plugins.yaml new file mode 100644 index 000000000..b88051064 --- /dev/null +++ b/pkg/library/flatten/testdata/parent/error_parent-has-plugins.yaml @@ -0,0 +1,26 @@ +name: "Fails when parent is not flattened (has plugin)" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + plugin: + id: parent-plugin + +output: + errRegexp: "parents containing plugins or parents are not supported" diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml new file mode 100644 index 000000000..d1d6da064 --- /dev/null +++ b/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml @@ -0,0 +1,68 @@ +name: "Resolve parent and plugins" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + - name: test-plugin + plugin: + uri: https://test-plugin.io/test-plugin + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + devfileResources: + "https://test-plugin.io/test-plugin": + schemaVersion: 2.1.0 + metadata: + name: test-plugin + components: + - name: plugin-component + container: + image: plugin-img + env: + - name: plugin-env + value: original-value +output: + workspace: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container + - name: plugin-component + attributes: + controller.devfile.io/imported-by: test-plugin + container: + image: plugin-img + env: + - name: plugin-env + value: original-value diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml new file mode 100644 index 000000000..2ddfbf35a --- /dev/null +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml @@ -0,0 +1,44 @@ +name: "Resolve parent by ID" + +input: + workspace: + parent: + id: test/parent/id + registryUrl: https://test-registry.io/ + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devfileResources: + "https://test-registry.io/test/parent/id": + schemaVersion: 2.1.0 + metadata: + name: parent-devfile + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml new file mode 100644 index 000000000..2b7dea41a --- /dev/null +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml @@ -0,0 +1,46 @@ +name: "Resolve parent by Kubernetes reference" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml new file mode 100644 index 000000000..e10bc48e9 --- /dev/null +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml @@ -0,0 +1,43 @@ +name: "Resolve parent by URI" + +input: + workspace: + parent: + uri: https://test.io/path/to/parent + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devfileResources: + "https://test.io/path/to/parent": + schemaVersion: 2.1.0 + metadata: + name: parent-devfile + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml b/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml index 570f2d1f9..c2b0ed51b 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml @@ -7,7 +7,7 @@ input: plugin: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" - plugins: + devworkspaceResources: "https://test-registry.io/subpath/my/test/plugin": metadata: name: test-plugin diff --git a/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml b/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml index 4eaff077f..ceb400f11 100644 --- a/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml +++ b/pkg/library/flatten/testdata/plugin-id/error_invalid-schema-version.yaml @@ -7,7 +7,7 @@ input: plugin: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" - devfilePlugins: + devfileResources: "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 1.0.0 metadata: diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml index 9ee833e80..8cb47ed55 100644 --- a/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml +++ b/pkg/library/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml @@ -7,7 +7,7 @@ input: plugin: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" - plugins: + devworkspaceResources: "https://test-registry.io/subpath/my/test/plugin": kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml index a8018e2f8..aa93e9bb0 100644 --- a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml +++ b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml @@ -7,7 +7,7 @@ input: plugin: id: my/test/plugin registryUrl: "https://test-registry.io/subpath" - devfilePlugins: + devfileResources: "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 2.0.0 metadata: diff --git a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml index d6e8a5279..d0fa411a8 100644 --- a/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml +++ b/pkg/library/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml @@ -11,7 +11,7 @@ input: plugin: id: my/test/plugin-2 registryUrl: "https://test-registry-2.io/subpath" - devfilePlugins: + devfileResources: "https://test-registry.io/subpath/my/test/plugin": schemaVersion: 2.0.0 metadata: diff --git a/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml b/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml index 2fc440542..c4aa2e2ec 100644 --- a/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml +++ b/pkg/library/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml @@ -6,7 +6,7 @@ input: - name: test-plugin plugin: uri: "https://my-plugin.io/test" - plugins: + devworkspaceResources: "https://my-plugin.io/test": metadata: name: test-plugin diff --git a/pkg/library/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml b/pkg/library/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml index ea5f777a8..9a92e17d4 100644 --- a/pkg/library/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml +++ b/pkg/library/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml @@ -6,7 +6,7 @@ input: - name: test-plugin plugin: uri: https://test-registry.io/old-devfiles - devfilePlugins: + devfileResources: "https://test-registry.io/old-devfiles": schemaVersion: 1.0.0 metadata: diff --git a/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml b/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml index 0c3bb6f23..5f97a7285 100644 --- a/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml +++ b/pkg/library/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml @@ -6,7 +6,7 @@ input: - name: test-plugin plugin: uri: "https://my-plugin.io/test" - plugins: + devworkspaceResources: "https://my-plugin.io/test": kind: DevWorkspaceTemplate apiVersion: workspace.devfile.io/v1alpha2 diff --git a/pkg/library/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml b/pkg/library/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml index 2be037828..410e10695 100644 --- a/pkg/library/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml +++ b/pkg/library/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml @@ -9,7 +9,7 @@ input: - name: test-plugin-2 plugin: uri: "https://my-plugin-alt.io/test" - devfilePlugins: + devfileResources: "https://my-plugin.io/test": schemaVersion: 2.0.0 metadata: diff --git a/pkg/library/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml b/pkg/library/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml index fd928c4d2..977638635 100644 --- a/pkg/library/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml +++ b/pkg/library/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml @@ -6,7 +6,7 @@ input: - name: test-plugin plugin: uri: "https://my-plugin.io/test" - devfilePlugins: + devfileResources: "https://my-plugin.io/test": schemaVersion: 2.0.0 metadata: From d286fa3c6d8e565bfa1defb1bf48b485c8abf681 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:31:14 -0400 Subject: [PATCH 07/12] Make tooling fields optional and fail gracefully Make flatten library work without an HTTP or Kubernetes client to allow supporting only kubernetes or http references. If attempting to flatten a devfile that contains an import that doesn't have the required client, return a descriptive error. Signed-off-by: Angel Misevski --- pkg/library/flatten/flatten.go | 50 ++++++++++++++++++++--------- pkg/library/flatten/flatten_test.go | 12 ++++--- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/pkg/library/flatten/flatten.go b/pkg/library/flatten/flatten.go index 1132baf85..47f3604e3 100644 --- a/pkg/library/flatten/flatten.go +++ b/pkg/library/flatten/flatten.go @@ -112,18 +112,18 @@ func recursiveResolve(workspace *devworkspace.DevWorkspaceTemplateSpec, tooling } // resolveParentComponent resolves the parent DevWorkspaceTemplateSpec that a parent reference refers to. -func resolveParentComponent(parent *devworkspace.Parent, tooling ResolverTools) (resolvedParent *devworkspace.DevWorkspaceTemplateSpec, err error) { +func resolveParentComponent(parent *devworkspace.Parent, tools ResolverTools) (resolvedParent *devworkspace.DevWorkspaceTemplateSpec, err error) { switch { case parent.Kubernetes != nil: // Search in default namespace if namespace ref is unset if parent.Kubernetes.Namespace == "" { - parent.Kubernetes.Namespace = tooling.DefaultNamespace + parent.Kubernetes.Namespace = tools.DefaultNamespace } - resolvedParent, err = resolveElementByKubernetesImport("parent", parent.Kubernetes, tooling) + resolvedParent, err = resolveElementByKubernetesImport("parent", parent.Kubernetes, tools) case parent.Uri != "": - resolvedParent, err = resolveElementByURI("parent", parent.Uri, tooling) + resolvedParent, err = resolveElementByURI("parent", parent.Uri, tools) case parent.Id != "": - resolvedParent, err = resolveElementById("parent", parent.Id, parent.RegistryUrl, tooling) + resolvedParent, err = resolveElementById("parent", parent.Id, parent.RegistryUrl, tools) default: err = fmt.Errorf("devfile parent does not define any resources") } @@ -146,18 +146,14 @@ func resolveParentComponent(parent *devworkspace.Parent, tooling ResolverTools) func resolvePluginComponent( name string, plugin *devworkspace.PluginComponent, - tooling ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { + tools ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { switch { case plugin.Kubernetes != nil: - // Search in default namespace if namespace ref is unset - if plugin.Kubernetes.Namespace == "" { - plugin.Kubernetes.Namespace = tooling.DefaultNamespace - } - resolvedPlugin, err = resolveElementByKubernetesImport(name, plugin.Kubernetes, tooling) + resolvedPlugin, err = resolveElementByKubernetesImport(name, plugin.Kubernetes, tools) case plugin.Uri != "": - resolvedPlugin, err = resolveElementByURI(name, plugin.Uri, tooling) + resolvedPlugin, err = resolveElementByURI(name, plugin.Uri, tools) case plugin.Id != "": - resolvedPlugin, err = resolveElementById(name, plugin.Id, plugin.RegistryUrl, tooling) + resolvedPlugin, err = resolveElementById(name, plugin.Id, plugin.RegistryUrl, tools) default: err = fmt.Errorf("plugin %s does not define any resources", name) } @@ -184,14 +180,27 @@ func resolvePluginComponent( func resolveElementByKubernetesImport( name string, kubeReference *devworkspace.KubernetesCustomResourceImportReference, - tooling ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { + tools ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { + + if tools.K8sClient == nil { + return nil, fmt.Errorf("cannot resolve resources by kubernetes reference: no kubernetes client provided") + } + + // Search in default namespace if namespace ref is unset + namespace := kubeReference.Namespace + if namespace == "" { + if tools.DefaultNamespace == "" { + return nil, fmt.Errorf("'%s' specifies a kubernetes reference without namespace and a default is not provided", name) + } + namespace = tools.DefaultNamespace + } var dwTemplate devworkspace.DevWorkspaceTemplate namespacedName := types.NamespacedName{ Name: kubeReference.Name, - Namespace: kubeReference.Namespace, + Namespace: namespace, } - err = tooling.K8sClient.Get(tooling.Context, namespacedName, &dwTemplate) + err = tools.K8sClient.Get(tools.Context, namespacedName, &dwTemplate) if err != nil { if errors.IsNotFound(err) { return nil, fmt.Errorf("plugin for component %s not found", name) @@ -225,6 +234,11 @@ func resolveElementById( return &pluginDWT.Spec, nil } + + if tools.HttpClient == nil { + return nil, fmt.Errorf("cannot resolve resources by id: no HTTP client provided") + } + pluginURL, err := url.Parse(registryUrl) if err != nil { return nil, fmt.Errorf("failed to parse registry URL for component %s: %w", name, err) @@ -245,6 +259,10 @@ func resolveElementByURI( uri string, tools ResolverTools) (resolvedPlugin *devworkspace.DevWorkspaceTemplateSpec, err error) { + if tools.HttpClient == nil { + return nil, fmt.Errorf("cannot resolve resources by id: no HTTP client provided") + } + dwt, err := network.FetchDevWorkspaceTemplate(uri, tools.HttpClient) if err != nil { return nil, fmt.Errorf("failed to resolve component %s by URI: %w", name, err) diff --git a/pkg/library/flatten/flatten_test.go b/pkg/library/flatten/flatten_test.go index adb89f035..5cbdb41aa 100644 --- a/pkg/library/flatten/flatten_test.go +++ b/pkg/library/flatten/flatten_test.go @@ -33,8 +33,9 @@ func TestResolveDevWorkspaceKubernetesReference(t *testing.T) { Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ - Context: context.Background(), - K8sClient: testClient, + Context: context.Background(), + DefaultNamespace: "test-ignored", + K8sClient: testClient, } outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) if tt.Output.ErrRegexp != nil && assert.Error(t, err) { @@ -159,9 +160,10 @@ func TestResolveDevWorkspaceParents(t *testing.T) { Errors: tt.Input.Errors, } testResolverTools := ResolverTools{ - Context: context.Background(), - K8sClient: testK8sClient, - HttpClient: testHttpGetter, + Context: context.Background(), + DefaultNamespace: "test-ignored", + K8sClient: testK8sClient, + HttpClient: testHttpGetter, } outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) if tt.Output.ErrRegexp != nil && assert.Error(t, err) { From 3a17bd4c54feb3f7f8ebc00502ea173ed3e4eb72 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:33:34 -0400 Subject: [PATCH 08/12] Add test cases to cover cases where incomplete tooling is provided Adds test cases to cover: - Using resolver when HttpClient or K8sClient are nil - Resolving Kubernetes resources when no namespace can be determined - Resolving resources by ID when no registryURL can be determined Signed-off-by: Angel Misevski --- pkg/library/flatten/flatten_test.go | 38 +++++++++++++++++++ ...icely-when-no-http-client-provided_id.yaml | 11 ++++++ ...cely-when-no-http-client-provided_uri.yaml | 11 ++++++ ...il-nicely-when-no-k8s-client-provided.yaml | 13 +++++++ .../fail-nicely-when-no-namespace.yaml | 12 ++++++ .../fail-nicely-when-no-registry-url.yaml | 11 ++++++ 6 files changed, 96 insertions(+) create mode 100644 pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml create mode 100644 pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml create mode 100644 pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml create mode 100644 pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml create mode 100644 pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml diff --git a/pkg/library/flatten/flatten_test.go b/pkg/library/flatten/flatten_test.go index 5cbdb41aa..c88d10f5b 100644 --- a/pkg/library/flatten/flatten_test.go +++ b/pkg/library/flatten/flatten_test.go @@ -179,3 +179,41 @@ func TestResolveDevWorkspaceParents(t *testing.T) { }) } } + +func TestResolveDevWorkspaceMissingDefaults(t *testing.T) { + tests := []testutil.TestCase{ + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-registry-url.yaml"), + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-namespace.yaml"), + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testK8sClient, + HttpClient: testHttpGetter, + } + outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml new file mode 100644 index 000000000..f65b04282 --- /dev/null +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no HTTP client provided and id is used" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: test/plugin + +output: + errRegexp: "cannot resolve resources by id: no HTTP client provided" \ No newline at end of file diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml new file mode 100644 index 000000000..ce243305d --- /dev/null +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no HTTP client provided and uri is used" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: https://test-repo.io/my-plugino + +output: + errRegexp: "cannot resolve resources by URI: no HTTP client provided" \ No newline at end of file diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml new file mode 100644 index 000000000..d97adde81 --- /dev/null +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml @@ -0,0 +1,13 @@ +name: "Fails nicely when no Kubernetes client provided" + +input: + workspace: + components: + - name: test-plugin + plugin: + kubernetes: + name: test-plugin-a + namespace: test-ns + +output: + errRegexp: "cannot resolve resources by kubernetes reference: no kubernetes client provided" \ No newline at end of file diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml new file mode 100644 index 000000000..d1d2fbab3 --- /dev/null +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml @@ -0,0 +1,12 @@ +name: "Fails nicely when no Kubernetes namespace is provided and there's no default" + +input: + workspace: + components: + - name: test-plugin + plugin: + kubernetes: + name: test-plugin-a + +output: + errRegexp: "specifies a kubernetes reference without namespace and a default is not provided" \ No newline at end of file diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml new file mode 100644 index 000000000..665a35fb5 --- /dev/null +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no registry URL is provided and there's no default" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: test/plugin + +output: + errRegexp: "plugin test-plugin does not specify a registryUrl and no internal registry is configured" \ No newline at end of file From d588a096865a7ddf5504314b2d846a0f4f2f31fd Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 16:36:05 -0400 Subject: [PATCH 09/12] Annotate elements merged from parent with their source Add attribute 'controller.devfile.io/imported-by: parent' to Components, Commands, Projects, and StarterProjects imported via a parent in the original devfile, to help track where resources in the resolved Devfile came from Signed-off-by: Angel Misevski fixup annotate parent --- pkg/library/annotate/plugins.go | 28 ++++++++++++++-------------- pkg/library/flatten/flatten.go | 3 ++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/library/annotate/plugins.go b/pkg/library/annotate/plugins.go index 3577f23a7..565c0c440 100644 --- a/pkg/library/annotate/plugins.go +++ b/pkg/library/annotate/plugins.go @@ -17,31 +17,31 @@ import ( "github.com/devfile/api/v2/pkg/attributes" ) -// AddSourceAttributesForPlugin adds an attribute 'controller.devfile.io/imported-by=sourceID' to all elements of +// AddSourceAttributesForTemplate adds an attribute 'controller.devfile.io/imported-by=sourceID' to all elements of // a plugin that support attributes. -func AddSourceAttributesForPlugin(sourceID string, plugin *dw.DevWorkspaceTemplateSpec) { - for idx, component := range plugin.Components { +func AddSourceAttributesForTemplate(sourceID string, template *dw.DevWorkspaceTemplateSpec) { + for idx, component := range template.Components { if component.Attributes == nil { - plugin.Components[idx].Attributes = attributes.Attributes{} + template.Components[idx].Attributes = attributes.Attributes{} } - plugin.Components[idx].Attributes.PutString(PluginSourceAttribute, sourceID) + template.Components[idx].Attributes.PutString(PluginSourceAttribute, sourceID) } - for idx, command := range plugin.Commands { + for idx, command := range template.Commands { if command.Attributes == nil { - plugin.Commands[idx].Attributes = attributes.Attributes{} + template.Commands[idx].Attributes = attributes.Attributes{} } - plugin.Commands[idx].Attributes.PutString(PluginSourceAttribute, sourceID) + template.Commands[idx].Attributes.PutString(PluginSourceAttribute, sourceID) } - for idx, project := range plugin.Projects { + for idx, project := range template.Projects { if project.Attributes == nil { - plugin.Projects[idx].Attributes = attributes.Attributes{} + template.Projects[idx].Attributes = attributes.Attributes{} } - plugin.Projects[idx].Attributes.PutString(PluginSourceAttribute, sourceID) + template.Projects[idx].Attributes.PutString(PluginSourceAttribute, sourceID) } - for idx, project := range plugin.StarterProjects { + for idx, project := range template.StarterProjects { if project.Attributes == nil { - plugin.Projects[idx].Attributes = attributes.Attributes{} + template.StarterProjects[idx].Attributes = attributes.Attributes{} } - plugin.Projects[idx].Attributes.PutString(PluginSourceAttribute, sourceID) + template.StarterProjects[idx].Attributes.PutString(PluginSourceAttribute, sourceID) } } diff --git a/pkg/library/flatten/flatten.go b/pkg/library/flatten/flatten.go index 47f3604e3..83ad155f0 100644 --- a/pkg/library/flatten/flatten.go +++ b/pkg/library/flatten/flatten.go @@ -68,6 +68,7 @@ func recursiveResolve(workspace *devworkspace.DevWorkspaceTemplateSpec, tooling // TODO: implemenent this return nil, fmt.Errorf("parents containing plugins or parents are not supported") } + annotate.AddSourceAttributesForTemplate("parent", resolvedParentSpec) resolvedParent = &resolvedParentSpec.DevWorkspaceTemplateSpecContent } resolvedContent := &devworkspace.DevWorkspaceTemplateSpecContent{} @@ -96,7 +97,7 @@ func recursiveResolve(workspace *devworkspace.DevWorkspaceTemplateSpec, tooling return nil, err } - annotate.AddSourceAttributesForPlugin(component.Name, resolvedPlugin) + annotate.AddSourceAttributesForTemplate(component.Name, resolvedPlugin) pluginSpecContents = append(pluginSpecContents, &resolvedPlugin.DevWorkspaceTemplateSpecContent) } } From c80e5ebe6f4926ebf82d687f2723e93d23e9429f Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Tue, 23 Mar 2021 23:15:31 -0400 Subject: [PATCH 10/12] Add tests to cover attributes on merged devfiles Signed-off-by: Angel Misevski --- pkg/library/flatten/flatten_test.go | 36 ++++++++++ ...nnotate-devfile-with-importing-source.yaml | 67 +++++++++++++++++++ .../parent/resolve-parent-and-plugins.yaml | 2 + .../testdata/parent/resolve-parent-by-id.yaml | 2 + .../resolve-parent-by-k8s-reference.yaml | 2 + .../parent/resolve-parent-by-uri.yaml | 2 + 6 files changed, 111 insertions(+) create mode 100644 pkg/library/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml diff --git a/pkg/library/flatten/flatten_test.go b/pkg/library/flatten/flatten_test.go index c88d10f5b..86c7ce55a 100644 --- a/pkg/library/flatten/flatten_test.go +++ b/pkg/library/flatten/flatten_test.go @@ -217,3 +217,39 @@ func TestResolveDevWorkspaceMissingDefaults(t *testing.T) { }) } } + +func TestResolveDevWorkspaceAnnotations(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/annotate") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testK8sClient, + HttpClient: testHttpGetter, + DefaultNamespace: "default-namespace", + } + outputWorkspace, err := ResolveDevWorkspace(tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} diff --git a/pkg/library/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml b/pkg/library/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml new file mode 100644 index 000000000..5a98fd431 --- /dev/null +++ b/pkg/library/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml @@ -0,0 +1,67 @@ +name: "Annotates resources merged into devfile with source attribute" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: test-plugin + plugin: + uri: https://test-plugin.io/test-plugin + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + projects: + - name: parent-project + starterProjects: + - name: parent-starter-project + components: + - name: parent-component + container: + image: test-img + commands: + - id: parent-command + devfileResources: + "https://test-plugin.io/test-plugin": + schemaVersion: 2.1.0 + metadata: + name: test-plugin + components: + - name: plugin-component + container: + image: plugin-img + commands: + - id: plugin-command +output: + workspace: + projects: + - name: parent-project + attributes: + controller.devfile.io/imported-by: parent + starterProjects: + - name: parent-starter-project + attributes: + controller.devfile.io/imported-by: parent + components: + - name: parent-component + attributes: + controller.devfile.io/imported-by: parent + container: + image: test-img + - name: plugin-component + attributes: + controller.devfile.io/imported-by: test-plugin + container: + image: plugin-img + commands: + - id: parent-command + attributes: + controller.devfile.io/imported-by: parent + - id: plugin-command + attributes: + controller.devfile.io/imported-by: test-plugin diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml index d1d6da064..11dd4dd17 100644 --- a/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml +++ b/pkg/library/flatten/testdata/parent/resolve-parent-and-plugins.yaml @@ -49,6 +49,8 @@ output: workspace: components: - name: parent-component + attributes: + controller.devfile.io/imported-by: parent container: image: test-img env: diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml index 2ddfbf35a..ba86550f0 100644 --- a/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-id.yaml @@ -33,6 +33,8 @@ output: workspace: components: - name: parent-component + attributes: + controller.devfile.io/imported-by: parent container: image: test-img env: diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml index 2b7dea41a..68e697cd2 100644 --- a/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml @@ -35,6 +35,8 @@ output: workspace: components: - name: parent-component + attributes: + controller.devfile.io/imported-by: parent container: image: test-img env: diff --git a/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml b/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml index e10bc48e9..63a9a65e4 100644 --- a/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml +++ b/pkg/library/flatten/testdata/parent/resolve-parent-by-uri.yaml @@ -32,6 +32,8 @@ output: workspace: components: - name: parent-component + attributes: + controller.devfile.io/imported-by: parent container: image: test-img env: From 2114f05f1916905d334fc4e88dafe8f050f46c78 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Wed, 24 Mar 2021 23:16:13 -0400 Subject: [PATCH 11/12] Fixup test case after changing function signatures Signed-off-by: Angel Misevski --- pkg/library/container/container_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/library/container/container_test.go b/pkg/library/container/container_test.go index fcefdabab..52e693596 100644 --- a/pkg/library/container/container_test.go +++ b/pkg/library/container/container_test.go @@ -29,9 +29,9 @@ import ( ) type testCase struct { - Name string `json:"name,omitempty"` - Input devworkspace.DevWorkspaceTemplateSpec `json:"input,omitempty"` - Output testOutput `json:"output,omitempty"` + Name string `json:"name,omitempty"` + Input *devworkspace.DevWorkspaceTemplateSpec `json:"input,omitempty"` + Output testOutput `json:"output,omitempty"` } type testOutput struct { From 173f24212f9ff81d81a840fd88d535e82b18fec8 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Thu, 25 Mar 2021 17:45:28 -0400 Subject: [PATCH 12/12] fixup: trailing newlines; should be squashed into 3a17bd4c Signed-off-by: Angel Misevski --- .../general/fail-nicely-when-no-http-client-provided_id.yaml | 2 +- .../general/fail-nicely-when-no-http-client-provided_uri.yaml | 2 +- .../general/fail-nicely-when-no-k8s-client-provided.yaml | 2 +- .../flatten/testdata/general/fail-nicely-when-no-namespace.yaml | 2 +- .../testdata/general/fail-nicely-when-no-registry-url.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml index f65b04282..cc88ffc17 100644 --- a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml @@ -8,4 +8,4 @@ input: id: test/plugin output: - errRegexp: "cannot resolve resources by id: no HTTP client provided" \ No newline at end of file + errRegexp: "cannot resolve resources by id: no HTTP client provided" diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml index ce243305d..ebb37d7b7 100644 --- a/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml @@ -8,4 +8,4 @@ input: uri: https://test-repo.io/my-plugino output: - errRegexp: "cannot resolve resources by URI: no HTTP client provided" \ No newline at end of file + errRegexp: "cannot resolve resources by URI: no HTTP client provided" diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml index d97adde81..561728974 100644 --- a/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml @@ -10,4 +10,4 @@ input: namespace: test-ns output: - errRegexp: "cannot resolve resources by kubernetes reference: no kubernetes client provided" \ No newline at end of file + errRegexp: "cannot resolve resources by kubernetes reference: no kubernetes client provided" diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml index d1d2fbab3..d0867c002 100644 --- a/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-namespace.yaml @@ -9,4 +9,4 @@ input: name: test-plugin-a output: - errRegexp: "specifies a kubernetes reference without namespace and a default is not provided" \ No newline at end of file + errRegexp: "specifies a kubernetes reference without namespace and a default is not provided" diff --git a/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml b/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml index 665a35fb5..e4485febb 100644 --- a/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml +++ b/pkg/library/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml @@ -8,4 +8,4 @@ input: id: test/plugin output: - errRegexp: "plugin test-plugin does not specify a registryUrl and no internal registry is configured" \ No newline at end of file + errRegexp: "plugin test-plugin does not specify a registryUrl and no internal registry is configured"