feat: add support for `coder_env` (#11102)

Fixes #10166
This commit is contained in:
Mathias Fredriksson 2023-12-11 16:10:18 +02:00 committed by GitHub
parent 4612c28d99
commit 3e5d292135
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 550 additions and 382 deletions

View File

@ -1388,13 +1388,27 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
Valid: true,
}
}
var env pqtype.NullRawMessage
if prAgent.Env != nil {
data, err := json.Marshal(prAgent.Env)
env := make(map[string]string)
// For now, we only support adding extra envs, not overriding
// existing ones or performing other manipulations. In future
// we may write these to a separate table so we can perform
// conditional logic on the agent.
for _, e := range prAgent.ExtraEnvs {
env[e.Name] = e.Value
}
// Allow the agent defined envs to override extra envs.
for k, v := range prAgent.Env {
env[k] = v
}
var envJSON pqtype.NullRawMessage
if len(env) > 0 {
data, err := json.Marshal(env)
if err != nil {
return xerrors.Errorf("marshal env: %w", err)
}
env = pqtype.NullRawMessage{
envJSON = pqtype.NullRawMessage{
RawMessage: data,
Valid: true,
}
@ -1417,7 +1431,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
AuthToken: authToken,
AuthInstanceID: instanceID,
Architecture: prAgent.Architecture,
EnvironmentVariables: env,
EnvironmentVariables: envJSON,
Directory: prAgent.Directory,
OperatingSystem: prAgent.OperatingSystem,
ConnectionTimeoutSeconds: prAgent.GetConnectionTimeoutSeconds(),

View File

@ -1585,6 +1585,16 @@ func TestInsertWorkspaceResource(t *testing.T) {
Apps: []*sdkproto.App{{
Slug: "a",
}},
ExtraEnvs: []*sdkproto.Env{
{
Name: "something", // Duplicate, already set by Env.
Value: "I should be discarded!",
},
{
Name: "else",
Value: "I laugh in the face of danger.",
},
},
Scripts: []*sdkproto.Script{{
DisplayName: "Startup",
Icon: "/test.png",
@ -1609,6 +1619,7 @@ func TestInsertWorkspaceResource(t *testing.T) {
require.Equal(t, "linux", agent.OperatingSystem)
want, err := json.Marshal(map[string]string{
"something": "test",
"else": "I laugh in the face of danger.",
})
require.NoError(t, err)
got, err := agent.EnvironmentVariables.RawMessage.MarshalJSON()

View File

@ -77,6 +77,12 @@ type agentAppAttributes struct {
Healthcheck []appHealthcheckAttributes `mapstructure:"healthcheck"`
}
type agentEnvAttributes struct {
AgentID string `mapstructure:"agent_id"`
Name string `mapstructure:"name"`
Value string `mapstructure:"value"`
}
type agentScriptAttributes struct {
AgentID string `mapstructure:"agent_id"`
DisplayName string `mapstructure:"display_name"`
@ -435,6 +441,32 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
}
}
// Associate envs with agents.
for _, resources := range tfResourcesByLabel {
for _, resource := range resources {
if resource.Type != "coder_env" {
continue
}
var attrs agentEnvAttributes
err = mapstructure.Decode(resource.AttributeValues, &attrs)
if err != nil {
return nil, xerrors.Errorf("decode env attributes: %w", err)
}
for _, agents := range resourceAgents {
for _, agent := range agents {
// Find agents with the matching ID and associate them!
if agent.Id != attrs.AgentID {
continue
}
agent.ExtraEnvs = append(agent.ExtraEnvs, &proto.Env{
Name: attrs.Name,
Value: attrs.Value,
})
}
}
}
}
// Associate scripts with agents.
for _, resources := range tfResourcesByLabel {
for _, resource := range resources {
@ -444,7 +476,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
var attrs agentScriptAttributes
err = mapstructure.Decode(resource.AttributeValues, &attrs)
if err != nil {
return nil, xerrors.Errorf("decode app attributes: %w", err)
return nil, xerrors.Errorf("decode script attributes: %w", err)
}
for _, agents := range resourceAgents {
for _, agent := range agents {

File diff suppressed because it is too large Load Diff

View File

@ -119,6 +119,7 @@ message Agent {
// Field 19 was startup_script_behavior, now removed.
DisplayApps display_apps = 20;
repeated Script scripts = 21;
repeated Env extra_envs = 22;
}
enum AppSharingLevel {
@ -135,6 +136,11 @@ message DisplayApps {
bool port_forwarding_helper = 5;
}
message Env {
string name = 1;
string value = 2;
}
// Script represents a script to be run on the workspace.
message Script {
string display_name = 1;

View File

@ -477,6 +477,7 @@ const createTemplateVersionTar = async (
env: {},
id: randomUUID(),
metadata: [],
extraEnvs: [],
scripts: [],
motdFile: "",
name: "dev",

View File

@ -122,6 +122,7 @@ export interface Agent {
/** Field 19 was startup_script_behavior, now removed. */
displayApps: DisplayApps | undefined;
scripts: Script[];
extraEnvs: Env[];
}
export interface Agent_Metadata {
@ -145,6 +146,11 @@ export interface DisplayApps {
portForwardingHelper: boolean;
}
export interface Env {
name: string;
value: string;
}
/** Script represents a script to be run on the workspace. */
export interface Script {
displayName: string;
@ -523,6 +529,9 @@ export const Agent = {
for (const v of message.scripts) {
Script.encode(v!, writer.uint32(170).fork()).ldelim();
}
for (const v of message.extraEnvs) {
Env.encode(v!, writer.uint32(178).fork()).ldelim();
}
return writer;
},
};
@ -590,6 +599,18 @@ export const DisplayApps = {
},
};
export const Env = {
encode(message: Env, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.name !== "") {
writer.uint32(10).string(message.name);
}
if (message.value !== "") {
writer.uint32(18).string(message.value);
}
return writer;
},
};
export const Script = {
encode(
message: Script,