Skip to content

Commit cdc0cdb

Browse files
committed
fix #12208: gp env to respect workspace env vars
1 parent 24979dc commit cdc0cdb

File tree

12 files changed

+441
-85
lines changed

12 files changed

+441
-85
lines changed

components/gitpod-cli/cmd/env.go

+16-10
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ delete environment variables with a repository pattern of */foo, foo/* or */*.
7878

7979
type connectToServerResult struct {
8080
repositoryPattern string
81+
wsInfo *supervisor.WorkspaceInfoResponse
8182
client *serverapi.APIoverJSONRPC
8283
}
8384

@@ -97,14 +98,15 @@ func connectToServer(ctx context.Context) (*connectToServerResult, error) {
9798
return nil, xerrors.New("repository info is missing owner")
9899
}
99100
if wsinfo.Repository.Name == "" {
100-
return nil, xerrors.New("repository info is missing name")
101+
xerrors.New("repository info is missing name")
101102
}
102103
repositoryPattern := wsinfo.Repository.Owner + "/" + wsinfo.Repository.Name
103104
clientToken, err := supervisor.NewTokenServiceClient(supervisorConn).GetToken(ctx, &supervisor.GetTokenRequest{
104105
Host: wsinfo.GitpodApi.Host,
105106
Kind: "gitpod",
106107
Scope: []string{
107-
"function:getEnvVars",
108+
"function:getWorkspaceEnvVars",
109+
"function:getEnvVars", // TODO remove then getWorkspaceEnvVars is deployed
108110
"function:setEnvVar",
109111
"function:deleteEnvVar",
110112
"resource:envVar::" + repositoryPattern + "::create/get/update/delete",
@@ -121,7 +123,7 @@ func connectToServer(ctx context.Context) (*connectToServerResult, error) {
121123
if err != nil {
122124
return nil, xerrors.Errorf("failed connecting to server: %w", err)
123125
}
124-
return &connectToServerResult{repositoryPattern, client}, nil
126+
return &connectToServerResult{repositoryPattern, wsinfo, client}, nil
125127
}
126128

127129
func getEnvs(ctx context.Context) error {
@@ -131,13 +133,17 @@ func getEnvs(ctx context.Context) error {
131133
}
132134
defer result.client.Close()
133135

134-
vars, err := result.client.GetEnvVars(ctx)
136+
vars, err := result.client.GetWorkspaceEnvVars(ctx, result.wsInfo.WorkspaceId)
137+
if err != nil {
138+
// TODO remove then GetWorkspaceEnvVars is deployed
139+
vars, err = result.client.GetEnvVars(ctx)
140+
}
135141
if err != nil {
136142
return xerrors.Errorf("failed to fetch env vars from server: %w", err)
137143
}
138144

139145
for _, v := range vars {
140-
printVar(v, exportEnvs)
146+
printVar(v.Name, v.Value, exportEnvs)
141147
}
142148

143149
return nil
@@ -163,7 +169,7 @@ func setEnvs(ctx context.Context, args []string) error {
163169
if err != nil {
164170
return err
165171
}
166-
printVar(v, exportEnvs)
172+
printVar(v.Name, v.Value, exportEnvs)
167173
return nil
168174
})
169175
}
@@ -189,12 +195,12 @@ func deleteEnvs(ctx context.Context, args []string) error {
189195
return g.Wait()
190196
}
191197

192-
func printVar(v *serverapi.UserEnvVarValue, export bool) {
193-
val := strings.Replace(v.Value, "\"", "\\\"", -1)
198+
func printVar(name string, value string, export bool) {
199+
val := strings.Replace(value, "\"", "\\\"", -1)
194200
if export {
195-
fmt.Printf("export %s=\"%s\"\n", v.Name, val)
201+
fmt.Printf("export %s=\"%s\"\n", name, val)
196202
} else {
197-
fmt.Printf("%s=%s\n", v.Name, val)
203+
fmt.Printf("%s=%s\n", name, val)
198204
}
199205
}
200206

components/gitpod-protocol/go/gitpod-service.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ type APIInterface interface {
6565
ClosePort(ctx context.Context, workspaceID string, port float32) (err error)
6666
GetUserStorageResource(ctx context.Context, options *GetUserStorageResourceOptions) (res string, err error)
6767
UpdateUserStorageResource(ctx context.Context, options *UpdateUserStorageResourceOptions) (err error)
68-
GetEnvVars(ctx context.Context) (res []*UserEnvVarValue, err error)
68+
GetWorkspaceEnvVars(ctx context.Context, workspaceID string) (res []*EnvVar, err error)
69+
GetEnvVars(ctx context.Context) (res []*EnvVar, err error)
6970
SetEnvVar(ctx context.Context, variable *UserEnvVarValue) (err error)
7071
DeleteEnvVar(ctx context.Context, variable *UserEnvVarValue) (err error)
7172
HasSSHPublicKey(ctx context.Context) (res bool, err error)
@@ -1128,15 +1129,35 @@ func (gp *APIoverJSONRPC) UpdateUserStorageResource(ctx context.Context, options
11281129
return
11291130
}
11301131

1132+
// GetWorkspaceEnvVars calls GetWorkspaceEnvVars on the server
1133+
func (gp *APIoverJSONRPC) GetWorkspaceEnvVars(ctx context.Context, workspaceID string) (res []*EnvVar, err error) {
1134+
if gp == nil {
1135+
err = errNotConnected
1136+
return
1137+
}
1138+
var _params []interface{}
1139+
1140+
_params = append(_params, workspaceID)
1141+
1142+
var result []*EnvVar
1143+
err = gp.C.Call(ctx, "GetWorkspaceEnvVars", _params, &result)
1144+
if err != nil {
1145+
return
1146+
}
1147+
res = result
1148+
1149+
return
1150+
}
1151+
11311152
// GetEnvVars calls getEnvVars on the server
1132-
func (gp *APIoverJSONRPC) GetEnvVars(ctx context.Context) (res []*UserEnvVarValue, err error) {
1153+
func (gp *APIoverJSONRPC) GetEnvVars(ctx context.Context) (res []*EnvVar, err error) {
11331154
if gp == nil {
11341155
err = errNotConnected
11351156
return
11361157
}
11371158
var _params []interface{}
11381159

1139-
var result []*UserEnvVarValue
1160+
var result []*EnvVar
11401161
err = gp.C.Call(ctx, "getEnvVars", _params, &result)
11411162
if err != nil {
11421163
return
@@ -1978,6 +1999,13 @@ type WhitelistedRepository struct {
19781999
URL string `json:"url,omitempty"`
19792000
}
19802001

2002+
// EnvVar is the EnvVar message type
2003+
type EnvVar struct {
2004+
ID string `json:"id,omitempty"`
2005+
Name string `json:"name,omitempty"`
2006+
Value string `json:"value,omitempty"`
2007+
}
2008+
19812009
// UserEnvVarValue is the UserEnvVarValue message type
19822010
type UserEnvVarValue struct {
19832011
ID string `json:"id,omitempty"`

components/gitpod-protocol/go/mock.go

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/gitpod-protocol/src/gitpod-service.ts

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
UserSSHPublicKeyValue,
2828
SSHPublicKeyValue,
2929
IDESettings,
30+
EnvVarWithValue,
3031
} from "./protocol";
3132
import {
3233
Team,
@@ -152,6 +153,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
152153
getUserStorageResource(options: GitpodServer.GetUserStorageResourceOptions): Promise<string>;
153154
updateUserStorageResource(options: GitpodServer.UpdateUserStorageResourceOptions): Promise<void>;
154155

156+
// Workspace env vars
157+
getWorkspaceEnvVars(workspaceId: string): Promise<EnvVarWithValue[]>;
158+
155159
// User env vars
156160
getEnvVars(): Promise<UserEnvVarValue[]>;
157161
getAllEnvVars(): Promise<UserEnvVarValue[]>;

components/gitpod-protocol/src/protocol.ts

+2
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ export namespace NamedWorkspaceFeatureFlag {
311311
}
312312
}
313313

314+
export type EnvVar = UserEnvVar | ProjectEnvVarWithValue | EnvVarWithValue;
315+
314316
export interface EnvVarWithValue {
315317
name: string;
316318
value: string;

components/server/ee/src/prebuilds/prebuild-manager.ts

+17-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
CommitInfo,
1111
PrebuiltWorkspace,
1212
Project,
13-
ProjectEnvVar,
1413
StartPrebuildContext,
1514
StartPrebuildResult,
1615
TaskConfig,
@@ -39,6 +38,7 @@ import { ResponseError } from "vscode-ws-jsonrpc";
3938
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
4039
import { UserService } from "../../../src/user/user-service";
4140
import { EntitlementService, MayStartWorkspaceResult } from "../../../src/billing/entitlement-service";
41+
import { EnvVarService, ResolvedEnvVars } from "../../../src/workspace/env-var-service";
4242

4343
export class WorkspaceRunningError extends Error {
4444
constructor(msg: string, public instance: WorkspaceInstance) {
@@ -67,6 +67,7 @@ export class PrebuildManager {
6767
@inject(UserService) protected readonly userService: UserService;
6868
@inject(TeamDB) protected readonly teamDB: TeamDB;
6969
@inject(EntitlementService) protected readonly entitlementService: EntitlementService;
70+
@inject(EnvVarService) private readonly envVarService: EnvVarService;
7071

7172
async abortPrebuildsForBranch(ctx: TraceContext, project: Project, user: User, branch: string): Promise<void> {
7273
const span = TraceContext.startSpan("abortPrebuildsForBranch", ctx);
@@ -221,8 +222,6 @@ export class PrebuildManager {
221222
}
222223
}
223224

224-
const projectEnvVarsPromise = project ? this.projectService.getProjectEnvironmentVariables(project.id) : [];
225-
226225
let organizationId = (await this.teamDB.findTeamById(project.id))?.id;
227226
if (!user.additionalData?.isMigratedToTeamOnlyAttribution) {
228227
// If the user is not migrated to team-only attribution, we retrieve the organization from the attribution logic.
@@ -238,6 +237,9 @@ export class PrebuildManager {
238237
prebuildContext,
239238
context.normalizedContextURL!,
240239
);
240+
241+
const envVarsPromise = this.resolveEvnVars(workspace);
242+
241243
const prebuild = await this.workspaceDB.trace({ span }).findPrebuildByWorkspaceID(workspace.id)!;
242244
if (!prebuild) {
243245
throw new Error(`Failed to create a prebuild for: ${context.normalizedContextURL}`);
@@ -281,8 +283,8 @@ export class PrebuildManager {
281283
await this.workspaceDB.trace({ span }).storePrebuiltWorkspace(prebuild);
282284
} else {
283285
span.setTag("starting", true);
284-
const projectEnvVars = await projectEnvVarsPromise;
285-
await this.workspaceStarter.startWorkspace({ span }, workspace, user, project, [], projectEnvVars, {
286+
const envVars = await envVarsPromise;
287+
await this.workspaceStarter.startWorkspace({ span }, workspace, user, project, envVars, {
286288
excludeFeatureFlags: ["full_workspace_backup"],
287289
});
288290
}
@@ -341,11 +343,8 @@ export class PrebuildManager {
341343
if (!prebuild) {
342344
throw new Error("No prebuild found for workspace " + workspaceId);
343345
}
344-
let projectEnvVars: ProjectEnvVar[] = [];
345-
if (workspace.projectId) {
346-
projectEnvVars = await this.projectService.getProjectEnvironmentVariables(workspace.projectId);
347-
}
348-
await this.workspaceStarter.startWorkspace({ span }, workspace, user, project, [], projectEnvVars);
346+
const envVars = await this.resolveEvnVars(workspace);
347+
await this.workspaceStarter.startWorkspace({ span }, workspace, user, project, envVars);
349348
return { prebuildId: prebuild.id, wsid: workspace.id, done: false };
350349
} catch (err) {
351350
TraceContext.setError({ span }, err);
@@ -477,4 +476,12 @@ export class PrebuildManager {
477476
span.finish();
478477
}
479478
}
479+
480+
private resolveEvnVars(workspace: Workspace): Promise<ResolvedEnvVars> {
481+
return this.envVarService.resolve({
482+
user: undefined, // no user specific in prebuild
483+
workspace,
484+
includeCensored: true,
485+
});
486+
}
480487
}

components/server/src/auth/rate-limiter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const defaultFunctions: FunctionsConfig = {
8989
closePort: { group: "default", points: 1 },
9090
getUserStorageResource: { group: "default", points: 1 },
9191
updateUserStorageResource: { group: "default", points: 1 },
92+
getWorkspaceEnvVars: { group: "default", points: 1 },
9293
getEnvVars: { group: "default", points: 1 },
9394
getAllEnvVars: { group: "default", points: 1 },
9495
setEnvVar: { group: "default", points: 1 },

components/server/src/container-module.ts

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ import { retryMiddleware } from "nice-grpc-client-middleware-retry";
114114
import { IamSessionApp } from "./iam/iam-session-app";
115115
import { spicedbClientFromEnv, SpiceDBClient } from "./authorization/spicedb";
116116
import { Authorizer, PermissionChecker } from "./authorization/perms";
117+
import { EnvVarService } from "./workspace/env-var-service";
117118

118119
export const productionContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => {
119120
bind(Config).toConstantValue(ConfigFile.fromFile());
@@ -256,6 +257,8 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo
256257

257258
bind(ProjectsService).toSelf().inSingletonScope();
258259

260+
bind(EnvVarService).toSelf().inSingletonScope();
261+
259262
bind(NewsletterSubscriptionController).toSelf().inSingletonScope();
260263

261264
bind<UsageServiceClient>(UsageServiceDefinition.name)

0 commit comments

Comments
 (0)