diff --git a/cmd/api/api/instances.go b/cmd/api/api/instances.go index ba5b448a..4eaafdef 100644 --- a/cmd/api/api/instances.go +++ b/cmd/api/api/instances.go @@ -798,6 +798,48 @@ func (s *ApiService) StatInstancePath(ctx context.Context, request oapi.StatInst return response, nil } +// UpdateInstanceEnv replaces the instance's env vars and updates egress proxy rules. +// The id parameter can be an instance ID, name, or ID prefix. +// Note: Resolution is handled by ResolveResource middleware. +func (s *ApiService) UpdateInstanceEnv(ctx context.Context, request oapi.UpdateInstanceEnvRequestObject) (oapi.UpdateInstanceEnvResponseObject, error) { + inst := mw.GetResolvedInstance[instances.Instance](ctx) + if inst == nil { + return oapi.UpdateInstanceEnv500JSONResponse{ + Code: "internal_error", + Message: "resource not resolved", + }, nil + } + log := logger.FromContext(ctx) + + result, err := s.InstanceManager.UpdateInstanceEnv(ctx, inst.Id, request.Body.Env) + if err != nil { + switch { + case errors.Is(err, instances.ErrNotFound): + return oapi.UpdateInstanceEnv404JSONResponse{ + Code: "not_found", + Message: "instance not found", + }, nil + case errors.Is(err, instances.ErrInvalidRequest): + return oapi.UpdateInstanceEnv400JSONResponse{ + Code: "invalid_request", + Message: err.Error(), + }, nil + case errors.Is(err, instances.ErrInvalidState): + return oapi.UpdateInstanceEnv409JSONResponse{ + Code: "invalid_state", + Message: err.Error(), + }, nil + default: + log.ErrorContext(ctx, "failed to update instance env", "error", err) + return oapi.UpdateInstanceEnv500JSONResponse{ + Code: "internal_error", + Message: "failed to update instance env", + }, nil + } + } + return oapi.UpdateInstanceEnv200JSONResponse(instanceToOAPI(*result)), nil +} + // AttachVolume attaches a volume to an instance (not yet implemented) func (s *ApiService) AttachVolume(ctx context.Context, request oapi.AttachVolumeRequestObject) (oapi.AttachVolumeResponseObject, error) { return oapi.AttachVolume500JSONResponse{ diff --git a/lib/builds/manager_test.go b/lib/builds/manager_test.go index a3765d5f..847359e0 100644 --- a/lib/builds/manager_test.go +++ b/lib/builds/manager_test.go @@ -167,6 +167,10 @@ func (m *mockInstanceManager) GetVsockDialer(ctx context.Context, instanceID str return nil, nil } +func (m *mockInstanceManager) UpdateInstanceEnv(ctx context.Context, id string, env map[string]string) (*instances.Instance, error) { + return nil, nil +} + // mockVolumeManager implements volumes.Manager for testing type mockVolumeManager struct { volumes map[string]*volumes.Volume diff --git a/lib/egressproxy/service.go b/lib/egressproxy/service.go index 75cd2e7f..f2b6455b 100644 --- a/lib/egressproxy/service.go +++ b/lib/egressproxy/service.go @@ -217,6 +217,27 @@ func compileHeaderInjectRules(cfgRules []HeaderInjectRuleConfig) ([]headerInject return out, nil } +// UpdateInjectRules atomically replaces the header-inject rules for a +// registered instance. Unlike RegisterInstance it does NOT re-apply iptables +// enforcement, which makes it suitable for credential rotation on a running VM. +// Returns ErrInstanceNotRegistered if the instance has not been registered. +func (s *Service) UpdateInjectRules(instanceID string, rules []HeaderInjectRuleConfig) error { + compiled, err := compileHeaderInjectRules(rules) + if err != nil { + return err + } + + s.mu.Lock() + defer s.mu.Unlock() + + sourceIP, ok := s.sourceIPByInstance[instanceID] + if !ok { + return ErrInstanceNotRegistered + } + s.policiesBySourceIP[sourceIP] = sourcePolicy{headerInjectRules: compiled} + return nil +} + func (s *Service) UnregisterInstance(_ context.Context, instanceID string) { s.mu.Lock() sourceIP, ok := s.sourceIPByInstance[instanceID] diff --git a/lib/egressproxy/service_test.go b/lib/egressproxy/service_test.go index 84aa487f..62d374d8 100644 --- a/lib/egressproxy/service_test.go +++ b/lib/egressproxy/service_test.go @@ -120,3 +120,38 @@ func TestHandleHTTPProxyRequest_DoesNotLeakUpstreamErrorDetails(t *testing.T) { require.NotContains(t, body, "dial failed") require.False(t, strings.Contains(body, "internal network detail")) } + +func TestUpdateInjectRules(t *testing.T) { + t.Parallel() + + matchers, err := compileDomainMatchers([]string{"api.openai.com"}) + require.NoError(t, err) + + svc := &Service{ + sourceIPByInstance: map[string]string{ + "inst-1": "10.0.0.2", + }, + policiesBySourceIP: map[string]sourcePolicy{ + "10.0.0.2": { + headerInjectRules: []headerInjectRule{ + {headerName: "Authorization", headerValue: "Bearer old-key", domainMatchers: matchers}, + }, + }, + }, + } + + // Update with a new key value. + err = svc.UpdateInjectRules("inst-1", []HeaderInjectRuleConfig{ + {HeaderName: "Authorization", HeaderValue: "Bearer rotated-key", AllowedDomains: []string{"api.openai.com"}}, + }) + require.NoError(t, err) + + // Verify the policy was swapped. + hdr := http.Header{} + svc.applyHeaderInjections("10.0.0.2", "api.openai.com", hdr, true) + require.Equal(t, "Bearer rotated-key", hdr.Get("Authorization")) + + // Unregistered instance returns an error. + err = svc.UpdateInjectRules("no-such-instance", nil) + require.ErrorIs(t, err, ErrInstanceNotRegistered) +} diff --git a/lib/egressproxy/types.go b/lib/egressproxy/types.go index 29c2e838..41dd5472 100644 --- a/lib/egressproxy/types.go +++ b/lib/egressproxy/types.go @@ -7,7 +7,8 @@ const ( ) var ( - ErrGatewayMismatch = errors.New("egress proxy already initialized with different gateway") + ErrGatewayMismatch = errors.New("egress proxy already initialized with different gateway") + ErrInstanceNotRegistered = errors.New("instance not registered with egress proxy") ) // InstanceConfig defines per-instance proxy behavior. diff --git a/lib/instances/egress_proxy_integration_test.go b/lib/instances/egress_proxy_integration_test.go index 96fdd86c..9cec3132 100644 --- a/lib/instances/egress_proxy_integration_test.go +++ b/lib/instances/egress_proxy_integration_test.go @@ -128,6 +128,25 @@ func TestEgressProxyRewritesHTTPSHeaders(t *testing.T) { require.Equal(t, 0, blockedExitCode, "curl output: %s", blockedOutput) require.Equal(t, "", blockedOutput) + // --- Key rotation: update env with a new secret value --- + rotatedInst, err := manager.UpdateInstanceEnv(ctx, inst.Id, map[string]string{ + "OUTBOUND_OPENAI_KEY": "rotated-key-456", + }) + require.NoError(t, err) + require.Equal(t, "rotated-key-456", rotatedInst.Env["OUTBOUND_OPENAI_KEY"]) + + // The proxy should now inject the rotated key. + rotatedOutput, rotatedExitCode, err := execCommand(ctx, inst, "sh", "-lc", allowedCmd) + require.NoError(t, err) + require.Equal(t, 0, rotatedExitCode, "curl output: %s", rotatedOutput) + require.Contains(t, rotatedOutput, "Bearer rotated-key-456") + + // The guest env must NOT change (still sees mock value). + envAfter, envAfterCode, err := execCommand(ctx, inst, "sh", "-lc", "printf '%s' \"$OUTBOUND_OPENAI_KEY\"") + require.NoError(t, err) + require.Equal(t, 0, envAfterCode) + require.Equal(t, "mock-OUTBOUND_OPENAI_KEY", envAfter) + require.NoError(t, manager.DeleteInstance(ctx, inst.Id)) deleted = true } diff --git a/lib/instances/manager.go b/lib/instances/manager.go index 8305a0c5..7b51c9ef 100644 --- a/lib/instances/manager.go +++ b/lib/instances/manager.go @@ -41,6 +41,7 @@ type Manager interface { StartInstance(ctx context.Context, id string, req StartInstanceRequest) (*Instance, error) StreamInstanceLogs(ctx context.Context, id string, tail int, follow bool, source LogSource) (<-chan string, error) RotateLogs(ctx context.Context, maxBytes int64, maxFiles int) error + UpdateInstanceEnv(ctx context.Context, id string, env map[string]string) (*Instance, error) AttachVolume(ctx context.Context, id string, volumeId string, req AttachVolumeRequest) (*Instance, error) DetachVolume(ctx context.Context, id string, volumeId string) (*Instance, error) // ListInstanceAllocations returns resource allocations for all instances. @@ -441,6 +442,49 @@ func (m *manager) RotateLogs(ctx context.Context, maxBytes int64, maxFiles int) return lastErr } +// UpdateInstanceEnv replaces the instance's env vars, re-validates credential +// bindings, persists the change, and atomically updates the egress proxy inject +// rules so rotated secrets take effect without a restart. +func (m *manager) UpdateInstanceEnv(ctx context.Context, id string, env map[string]string) (*Instance, error) { + lock := m.getInstanceLock(id) + lock.Lock() + defer lock.Unlock() + return m.updateInstanceEnv(ctx, id, env) +} + +func (m *manager) updateInstanceEnv(ctx context.Context, id string, env map[string]string) (*Instance, error) { + meta, err := m.loadMetadata(id) + if err != nil { + return nil, err + } + + // Validate credential bindings against the new env. + if len(meta.Credentials) > 0 { + if err := validateCredentialEnvBindings(meta.Credentials, env); err != nil { + return nil, err + } + } + + meta.Env = env + if err := m.saveMetadata(meta); err != nil { + return nil, fmt.Errorf("persist env update: %w", err) + } + + // If the egress proxy is active, update inject rules in-place. + if meta.NetworkEgress != nil && meta.NetworkEgress.Enabled && len(meta.Credentials) > 0 { + svc, err := m.getOrCreateEgressProxyService() + if err != nil { + return nil, fmt.Errorf("get egress proxy service: %w", err) + } + rules := buildEgressProxyInjectRules(meta.NetworkEgress, meta.Credentials, meta.Env) + if err := svc.UpdateInjectRules(id, rules); err != nil { + return nil, fmt.Errorf("update egress proxy inject rules: %w", err) + } + } + + return m.getInstance(ctx, id) +} + // AttachVolume attaches a volume to an instance (not yet implemented) func (m *manager) AttachVolume(ctx context.Context, id string, volumeId string, req AttachVolumeRequest) (*Instance, error) { return nil, fmt.Errorf("attach volume not yet implemented") diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 161067ad..cac968af 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -1177,6 +1177,12 @@ type ListInstancesParams struct { Tags *Tags `json:"tags,omitempty"` } +// UpdateInstanceEnvJSONBody defines parameters for UpdateInstanceEnv. +type UpdateInstanceEnvJSONBody struct { + // Env Full replacement env var map. All credential source env vars must be present. + Env map[string]string `json:"env"` +} + // GetInstanceLogsParams defines parameters for GetInstanceLogs. type GetInstanceLogsParams struct { // Tail Number of lines to return from end @@ -1264,6 +1270,9 @@ type CreateIngressJSONRequestBody = CreateIngressRequest // CreateInstanceJSONRequestBody defines body for CreateInstance for application/json ContentType. type CreateInstanceJSONRequestBody = CreateInstanceRequest +// UpdateInstanceEnvJSONRequestBody defines body for UpdateInstanceEnv for application/json ContentType. +type UpdateInstanceEnvJSONRequestBody UpdateInstanceEnvJSONBody + // ForkInstanceJSONRequestBody defines body for ForkInstance for application/json ContentType. type ForkInstanceJSONRequestBody = ForkInstanceRequest @@ -1435,6 +1444,11 @@ type ClientInterface interface { // GetInstance request GetInstance(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateInstanceEnvWithBody request with any body + UpdateInstanceEnvWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + UpdateInstanceEnv(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // ForkInstanceWithBody request with any body ForkInstanceWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1840,6 +1854,30 @@ func (c *Client) GetInstance(ctx context.Context, id string, reqEditors ...Reque return c.Client.Do(req) } +func (c *Client) UpdateInstanceEnvWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateInstanceEnvRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateInstanceEnv(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateInstanceEnvRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ForkInstanceWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewForkInstanceRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -3088,6 +3126,53 @@ func NewGetInstanceRequest(server string, id string) (*http.Request, error) { return req, nil } +// NewUpdateInstanceEnvRequest calls the generic UpdateInstanceEnv builder with application/json body +func NewUpdateInstanceEnvRequest(server string, id string, body UpdateInstanceEnvJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateInstanceEnvRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewUpdateInstanceEnvRequestWithBody generates requests for UpdateInstanceEnv with any type of body +func NewUpdateInstanceEnvRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/instances/%s/env", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewForkInstanceRequest calls the generic ForkInstance builder with application/json body func NewForkInstanceRequest(server string, id string, body ForkInstanceJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -4277,6 +4362,11 @@ type ClientWithResponsesInterface interface { // GetInstanceWithResponse request GetInstanceWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetInstanceResponse, error) + // UpdateInstanceEnvWithBodyWithResponse request with any body + UpdateInstanceEnvWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) + + UpdateInstanceEnvWithResponse(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) + // ForkInstanceWithBodyWithResponse request with any body ForkInstanceWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ForkInstanceResponse, error) @@ -4916,6 +5006,32 @@ func (r GetInstanceResponse) StatusCode() int { return 0 } +type UpdateInstanceEnvResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Instance + JSON400 *Error + JSON404 *Error + JSON409 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r UpdateInstanceEnvResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateInstanceEnvResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ForkInstanceResponse struct { Body []byte HTTPResponse *http.Response @@ -5701,6 +5817,23 @@ func (c *ClientWithResponses) GetInstanceWithResponse(ctx context.Context, id st return ParseGetInstanceResponse(rsp) } +// UpdateInstanceEnvWithBodyWithResponse request with arbitrary body returning *UpdateInstanceEnvResponse +func (c *ClientWithResponses) UpdateInstanceEnvWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) { + rsp, err := c.UpdateInstanceEnvWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateInstanceEnvResponse(rsp) +} + +func (c *ClientWithResponses) UpdateInstanceEnvWithResponse(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) { + rsp, err := c.UpdateInstanceEnv(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateInstanceEnvResponse(rsp) +} + // ForkInstanceWithBodyWithResponse request with arbitrary body returning *ForkInstanceResponse func (c *ClientWithResponses) ForkInstanceWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ForkInstanceResponse, error) { rsp, err := c.ForkInstanceWithBody(ctx, id, contentType, body, reqEditors...) @@ -6917,6 +7050,60 @@ func ParseGetInstanceResponse(rsp *http.Response) (*GetInstanceResponse, error) return response, nil } +// ParseUpdateInstanceEnvResponse parses an HTTP response from a UpdateInstanceEnvWithResponse call +func ParseUpdateInstanceEnvResponse(rsp *http.Response) (*UpdateInstanceEnvResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateInstanceEnvResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Instance + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseForkInstanceResponse parses an HTTP response from a ForkInstanceWithResponse call func ParseForkInstanceResponse(rsp *http.Response) (*ForkInstanceResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -7994,6 +8181,9 @@ type ServerInterface interface { // Get instance details // (GET /instances/{id}) GetInstance(w http.ResponseWriter, r *http.Request, id string) + // Update instance environment variables + // (PUT /instances/{id}/env) + UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) ForkInstance(w http.ResponseWriter, r *http.Request, id string) @@ -8204,6 +8394,12 @@ func (_ Unimplemented) GetInstance(w http.ResponseWriter, r *http.Request, id st w.WriteHeader(http.StatusNotImplemented) } +// Update instance environment variables +// (PUT /instances/{id}/env) +func (_ Unimplemented) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) { + w.WriteHeader(http.StatusNotImplemented) +} + // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) func (_ Unimplemented) ForkInstance(w http.ResponseWriter, r *http.Request, id string) { @@ -9004,6 +9200,37 @@ func (siw *ServerInterfaceWrapper) GetInstance(w http.ResponseWriter, r *http.Re handler.ServeHTTP(w, r) } +// UpdateInstanceEnv operation middleware +func (siw *ServerInterfaceWrapper) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "id" ------------- + var id string + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateInstanceEnv(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // ForkInstance operation middleware func (siw *ServerInterfaceWrapper) ForkInstance(w http.ResponseWriter, r *http.Request) { @@ -9994,6 +10221,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/instances/{id}", wrapper.GetInstance) }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/instances/{id}/env", wrapper.UpdateInstanceEnv) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/instances/{id}/fork", wrapper.ForkInstance) }) @@ -10981,6 +11211,60 @@ func (response GetInstance500JSONResponse) VisitGetInstanceResponse(w http.Respo return json.NewEncoder(w).Encode(response) } +type UpdateInstanceEnvRequestObject struct { + Id string `json:"id"` + Body *UpdateInstanceEnvJSONRequestBody +} + +type UpdateInstanceEnvResponseObject interface { + VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error +} + +type UpdateInstanceEnv200JSONResponse Instance + +func (response UpdateInstanceEnv200JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv400JSONResponse Error + +func (response UpdateInstanceEnv400JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv404JSONResponse Error + +func (response UpdateInstanceEnv404JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv409JSONResponse Error + +func (response UpdateInstanceEnv409JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv500JSONResponse Error + +func (response UpdateInstanceEnv500JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type ForkInstanceRequestObject struct { Id string `json:"id"` Body *ForkInstanceJSONRequestBody @@ -12030,6 +12314,9 @@ type StrictServerInterface interface { // Get instance details // (GET /instances/{id}) GetInstance(ctx context.Context, request GetInstanceRequestObject) (GetInstanceResponseObject, error) + // Update instance environment variables + // (PUT /instances/{id}/env) + UpdateInstanceEnv(ctx context.Context, request UpdateInstanceEnvRequestObject) (UpdateInstanceEnvResponseObject, error) // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) ForkInstance(ctx context.Context, request ForkInstanceRequestObject) (ForkInstanceResponseObject, error) @@ -12747,6 +13034,39 @@ func (sh *strictHandler) GetInstance(w http.ResponseWriter, r *http.Request, id } } +// UpdateInstanceEnv operation middleware +func (sh *strictHandler) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) { + var request UpdateInstanceEnvRequestObject + + request.Id = id + + var body UpdateInstanceEnvJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.UpdateInstanceEnv(ctx, request.(UpdateInstanceEnvRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateInstanceEnv") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(UpdateInstanceEnvResponseObject); ok { + if err := validResponse.VisitUpdateInstanceEnvResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // ForkInstance operation middleware func (sh *strictHandler) ForkInstance(w http.ResponseWriter, r *http.Request, id string) { var request ForkInstanceRequestObject @@ -13374,217 +13694,222 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "fzABAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/lib/scopes/scopes.go b/lib/scopes/scopes.go index 1ebbc49a..444285f4 100644 --- a/lib/scopes/scopes.go +++ b/lib/scopes/scopes.go @@ -239,6 +239,7 @@ var RouteScopes = map[string]Scope{ "GET /instances/{id}/stat": InstanceRead, "GET /instances/{id}/stats": InstanceRead, "POST /instances/{id}/stop": InstanceWrite, + "PUT /instances/{id}/env": InstanceWrite, "DELETE /instances/{id}/volumes/{volumeId}": VolumeWrite, "POST /instances/{id}/volumes/{volumeId}": VolumeWrite, diff --git a/openapi.yaml b/openapi.yaml index 1f0b5e1a..c8372f50 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2234,7 +2234,70 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - + + /instances/{id}/env: + put: + summary: Update instance environment variables + description: | + Replace the instance's environment variables. When the instance has an + egress proxy with credential injection, the proxy's header-inject rules + are recomputed immediately so rotated secrets take effect without a + restart. The guest still sees mock values for credential env vars. + operationId: updateInstanceEnv + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Instance ID or name + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [env] + properties: + env: + type: object + additionalProperties: + type: string + description: Full replacement env var map. All credential source env vars must be present. + responses: + 200: + description: Environment updated + content: + application/json: + schema: + $ref: "#/components/schemas/Instance" + 400: + description: Invalid request (e.g., missing credential source env) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 404: + description: Instance not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 409: + description: Conflict - instance not in correct state + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 500: + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /instances/{id}/logs: get: summary: Stream instance logs (SSE)