Skip to content

Support workspace env #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/library/flatten/flatten.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ func ResolveDevWorkspace(workspace *dw.DevWorkspaceTemplateSpec, tooling Resolve
return resolvedDW, &warnings, nil
}

err = resolveWorkspaceEnvVar(resolvedDW)
if err != nil {
return nil, nil, fmt.Errorf("failed to process workspaceEnv: %w", err)
}

return resolvedDW, nil, nil
}

Expand Down
155 changes: 62 additions & 93 deletions pkg/library/flatten/flatten_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,7 @@ func TestResolveDevWorkspaceKubernetesReference(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines workspace with no components")
testClient := &testutil.FakeK8sClient{
DevWorkspaceResources: tt.Input.DevWorkspaceResources,
Errors: tt.Input.Errors,
}
testResolverTools := ResolverTools{
Context: context.Background(),
WorkspaceNamespace: "test-ignored",
K8sClient: testClient,
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")
outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -59,14 +51,8 @@ func TestResolveDevWorkspaceInternalRegistry(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines workspace with no components")
testRegistry := &testutil.FakeInternalRegistry{
Plugins: tt.Input.DevWorkspaceResources,
Errors: tt.Input.Errors,
}
testResolverTools := ResolverTools{
Context: context.Background(),
InternalRegistry: testRegistry,
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -89,15 +75,8 @@ func TestResolveDevWorkspacePluginRegistry(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines workspace with no components")
testHttpGetter := &testutil.FakeHTTPGetter{
DevfileResources: tt.Input.DevfileResources,
DevWorkspaceResources: tt.Input.DevWorkspaceResources,
Errors: tt.Input.Errors,
}
testResolverTools := ResolverTools{
Context: context.Background(),
HttpClient: testHttpGetter,
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -120,15 +99,8 @@ func TestResolveDevWorkspacePluginURI(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines workspace with no components")
testHttpGetter := &testutil.FakeHTTPGetter{
DevfileResources: tt.Input.DevfileResources,
DevWorkspaceResources: tt.Input.DevWorkspaceResources,
Errors: tt.Input.Errors,
}
testResolverTools := ResolverTools{
Context: context.Background(),
HttpClient: testHttpGetter,
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -150,21 +122,8 @@ func TestResolveDevWorkspaceParents(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.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(),
WorkspaceNamespace: "test-ignored",
K8sClient: testK8sClient,
HttpClient: testHttpGetter,
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -189,20 +148,9 @@ func TestResolveDevWorkspaceMissingDefaults(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.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,
}
testResolverTools := getTestingTools(tt.Input, "")
testResolverTools.InternalRegistry = nil

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -224,21 +172,8 @@ func TestResolveDevWorkspaceAnnotations(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines devworkspace 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,
WorkspaceNamespace: "default-namespace",
}
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -260,21 +195,31 @@ func TestResolveDevWorkspaceTemplateNamespaceRestriction(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines devworkspace 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,
WorkspaceNamespace: "test-namespace",
testResolverTools := getTestingTools(tt.Input, "test-namespace")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, 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.DevWorkspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts),
"DevWorkspace should match expected output:\n%s",
cmp.Diff(tt.Output.DevWorkspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts))
}
})
}
}

func TestResolveDevWorkspaceWorkspaceEnv(t *testing.T) {
tests := testutil.LoadAllTestsOrPanic(t, "testdata/workspace-env")
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
// sanity check: input defines components
assert.True(t, len(tt.Input.DevWorkspace.Components) > 0, "Test case defines devworkspace with no components")
testResolverTools := getTestingTools(tt.Input, "test-ignored")

outputWorkspace, _, err := ResolveDevWorkspace(tt.Input.DevWorkspace, testResolverTools)
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
Expand All @@ -289,3 +234,27 @@ func TestResolveDevWorkspaceTemplateNamespaceRestriction(t *testing.T) {
})
}
}

func getTestingTools(input testutil.TestInput, testNamespace string) ResolverTools {
testHttpGetter := &testutil.FakeHTTPGetter{
DevfileResources: input.DevfileResources,
DevWorkspaceResources: input.DevWorkspaceResources,
Errors: input.Errors,
}
testK8sClient := &testutil.FakeK8sClient{
DevWorkspaceResources: input.DevWorkspaceResources,
Errors: input.Errors,
}
testRegistry := &testutil.FakeInternalRegistry{
Plugins: input.DevWorkspaceResources,

Errors: input.Errors,
}
return ResolverTools{
Context: context.Background(),
InternalRegistry: testRegistry,
K8sClient: testK8sClient,
HttpClient: testHttpGetter,
WorkspaceNamespace: testNamespace,
}
}
13 changes: 13 additions & 0 deletions pkg/library/flatten/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"reflect"

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/devworkspace-operator/pkg/constants"
)

// resolutionContextTree is a recursive structure representing information about the devworkspace that is
Expand Down Expand Up @@ -64,3 +65,15 @@ func formatImportCycle(end *resolutionContextTree) string {
}
return cycle
}

func getOriginalNameForComponent(component dw.Component) string {
if component.Attributes.Exists(constants.PluginSourceAttribute) {
var err error
componentName := component.Attributes.GetString(constants.PluginSourceAttribute, &err)
if err != nil {
return component.Name
}
return componentName
}
return component.Name
}
3 changes: 3 additions & 0 deletions pkg/library/flatten/internal/testutil/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ var WorkspaceTemplateDiffOpts = cmp.Options{
cmpopts.SortSlices(func(a, b string) bool {
return strings.Compare(a, b) > 0
}),
cmpopts.SortSlices(func(a, b dw.EnvVar) bool {
return strings.Compare(a.Name, b.Name) > 0
}),
// TODO: Devworkspace overriding results in empty []string instead of nil
cmpopts.IgnoreFields(dw.DevWorkspaceEvents{}, "PostStart", "PreStop", "PostStop"),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: "Adds workspace environment variables from plugin"

input:
devworkspace:
components:

- name: test-plugin
plugin:
uri: "https://my-plugin.io/test"

- name: regular-container
container:
image: test-image-regular

- name: init-container
container:
image: test-image-init

commands:
- id: make-init
apply:
component: init-container

events:
prestart: [make-init]

devfileResources:
"https://my-plugin.io/test":
schemaVersion: 2.0.0
metadata:
name: "plugin-a"
components:
- name: plugin-a
attributes:
workspaceEnv:
TEST_ENV_1: TEST_VAL_1
TEST_ENV_2: TEST_VAL_2
container:
name: test-container
image: test-image

output:
devworkspace:
components:
- name: plugin-a
attributes:
controller.devfile.io/imported-by: test-plugin
workspaceEnv:
TEST_ENV_1: TEST_VAL_1
TEST_ENV_2: TEST_VAL_2
container:
name: test-container
image: test-image
env:
- name: TEST_ENV_1
value: TEST_VAL_1
- name: TEST_ENV_2
value: TEST_VAL_2
- name: regular-container
container:
image: test-image-regular
env:
- name: TEST_ENV_1
value: TEST_VAL_1
- name: TEST_ENV_2
value: TEST_VAL_2
- name: init-container
container:
image: test-image-init
env:
- name: TEST_ENV_1
value: TEST_VAL_1
- name: TEST_ENV_2
value: TEST_VAL_2
commands:
- id: make-init
apply:
component: init-container
events:
prestart: [make-init]
Loading