diff --git a/cmd/agent_helpers.go b/cmd/agent_helpers.go index 8782a961..1e3523c2 100644 --- a/cmd/agent_helpers.go +++ b/cmd/agent_helpers.go @@ -46,7 +46,7 @@ func setupAgent( b := connectNATSBundle(ctx, log, connCfg, kvBucket, namespace, streamName) providerFactory := agent.NewProviderFactory(log) - hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, containerProvider := providerFactory.CreateProviders() + hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, dockerProvider := providerFactory.CreateProviders() // Create file provider if Object Store and file-state KV are configured hostname, _ := job.GetAgentHostname(appConfig.Agent.Hostname) @@ -67,7 +67,7 @@ func setupAgent( netinfoProvider, commandProvider, fileProvider, - containerProvider, + dockerProvider, b.registryKV, b.factsKV, ) diff --git a/cmd/api_helpers.go b/cmd/api_helpers.go index 2d682651..5dedc404 100644 --- a/cmd/api_helpers.go +++ b/cmd/api_helpers.go @@ -63,8 +63,8 @@ type ServerManager interface { GetMetricsHandler(metricsHandler http.Handler, path string) []func(e *echo.Echo) // GetAuditHandler returns audit handler for registration. GetAuditHandler(store audit.Store) []func(e *echo.Echo) - // GetContainerHandler returns container handler for registration. - GetContainerHandler(jobClient jobclient.JobClient) []func(e *echo.Echo) + // GetDockerHandler returns Docker handler for registration. + GetDockerHandler(jobClient jobclient.JobClient) []func(e *echo.Echo) // GetFileHandler returns file handler for registration. GetFileHandler(objStore file.ObjectStoreManager) []func(e *echo.Echo) // RegisterHandlers registers a list of handlers with the Echo instance. @@ -482,7 +482,7 @@ func registerAPIHandlers( handlers = append( handlers, sm.GetHealthHandler(checker, startTime, "0.1.0", metricsProvider)...) - handlers = append(handlers, sm.GetContainerHandler(jc)...) + handlers = append(handlers, sm.GetDockerHandler(jc)...) handlers = append(handlers, sm.GetMetricsHandler(metricsHandler, metricsPath)...) if auditStore != nil { handlers = append(handlers, sm.GetAuditHandler(auditStore)...) diff --git a/cmd/client_container.go b/cmd/client_container.go index 3d85de55..2b6b2ec5 100644 --- a/cmd/client_container.go +++ b/cmd/client_container.go @@ -22,11 +22,11 @@ package cmd import "github.com/spf13/cobra" -// clientContainerCmd represents the clientContainer command. +// clientContainerCmd represents the container parent command. var clientContainerCmd = &cobra.Command{ Use: "container", - Short: "Container management operations", - Long: `Manage containers on target nodes.`, + Short: "Container runtime management", + Long: `Manage containers using runtime-specific subcommands.`, } func init() { diff --git a/internal/provider/container/runtime/mocks/generate.go b/cmd/client_container_docker.go similarity index 75% rename from internal/provider/container/runtime/mocks/generate.go rename to cmd/client_container_docker.go index b47d6304..70c784a9 100644 --- a/internal/provider/container/runtime/mocks/generate.go +++ b/cmd/client_container_docker.go @@ -18,7 +18,17 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// Package mocks provides mock implementations for testing. -package mocks +package cmd -//go:generate go tool github.com/golang/mock/mockgen -source=../driver.go -destination=driver.gen.go -package=mocks +import "github.com/spf13/cobra" + +// clientContainerDockerCmd represents the docker subcommand under container. +var clientContainerDockerCmd = &cobra.Command{ + Use: "docker", + Short: "Docker container operations", + Long: `Manage Docker containers on target nodes.`, +} + +func init() { + clientContainerCmd.AddCommand(clientContainerDockerCmd) +} diff --git a/cmd/client_container_create.go b/cmd/client_container_docker_create.go similarity index 82% rename from cmd/client_container_create.go rename to cmd/client_container_docker_create.go index 53912f56..3fc93770 100644 --- a/cmd/client_container_create.go +++ b/cmd/client_container_docker_create.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerCreateCmd represents the clientContainerCreate command. -var clientContainerCreateCmd = &cobra.Command{ +// clientContainerDockerCreateCmd represents the clientContainerDockerCreate command. +var clientContainerDockerCreateCmd = &cobra.Command{ Use: "create", Short: "Create a new container", Long: `Create a new container on the target node from the specified image.`, @@ -44,7 +44,7 @@ var clientContainerCreateCmd = &cobra.Command{ volumeFlags, _ := cmd.Flags().GetStringSlice("volume") autoStart, _ := cmd.Flags().GetBool("auto-start") - body := gen.ContainerCreateRequest{ + body := gen.DockerCreateRequest{ Image: image, AutoStart: &autoStart, } @@ -62,7 +62,7 @@ var clientContainerCreateCmd = &cobra.Command{ body.Volumes = &volumeFlags } - resp, err := sdkClient.Container.Create(ctx, host, body) + resp, err := sdkClient.Docker.Create(ctx, host, body) if err != nil { cli.HandleError(err, logger) return @@ -98,20 +98,20 @@ var clientContainerCreateCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerCreateCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerCreateCmd) - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). String("image", "", "Container image reference (required)") - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). String("name", "", "Optional name for the container") - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). StringSlice("env", []string{}, "Environment variable in KEY=VALUE format (repeatable)") - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). StringSlice("port", []string{}, "Port mapping in host:container format (repeatable)") - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). StringSlice("volume", []string{}, "Volume mount in host:container format (repeatable)") - clientContainerCreateCmd.PersistentFlags(). + clientContainerDockerCreateCmd.PersistentFlags(). Bool("auto-start", true, "Start the container immediately after creation") - _ = clientContainerCreateCmd.MarkPersistentFlagRequired("image") + _ = clientContainerDockerCreateCmd.MarkPersistentFlagRequired("image") } diff --git a/cmd/client_container_exec.go b/cmd/client_container_docker_exec.go similarity index 81% rename from cmd/client_container_exec.go rename to cmd/client_container_docker_exec.go index 39db9bf4..e92cce0f 100644 --- a/cmd/client_container_exec.go +++ b/cmd/client_container_docker_exec.go @@ -30,8 +30,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerExecCmd represents the clientContainerExec command. -var clientContainerExecCmd = &cobra.Command{ +// clientContainerDockerExecCmd represents the clientContainerDockerExec command. +var clientContainerDockerExecCmd = &cobra.Command{ Use: "exec", Short: "Execute a command in a container", Long: `Execute a command inside a running container on the target node.`, @@ -43,7 +43,7 @@ var clientContainerExecCmd = &cobra.Command{ envFlags, _ := cmd.Flags().GetStringSlice("env") workingDir, _ := cmd.Flags().GetString("working-dir") - body := gen.ContainerExecRequest{ + body := gen.DockerExecRequest{ Command: command, } @@ -54,7 +54,7 @@ var clientContainerExecCmd = &cobra.Command{ body.WorkingDir = &workingDir } - resp, err := sdkClient.Container.Exec(ctx, host, id, body) + resp, err := sdkClient.Docker.Exec(ctx, host, id, body) if err != nil { cli.HandleError(err, logger) return @@ -89,17 +89,17 @@ var clientContainerExecCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerExecCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerExecCmd) - clientContainerExecCmd.PersistentFlags(). + clientContainerDockerExecCmd.PersistentFlags(). String("id", "", "Container ID to exec in (required)") - clientContainerExecCmd.PersistentFlags(). + clientContainerDockerExecCmd.PersistentFlags(). StringSlice("command", []string{}, "Command to execute (required, comma-separated)") - clientContainerExecCmd.PersistentFlags(). + clientContainerDockerExecCmd.PersistentFlags(). StringSlice("env", []string{}, "Environment variable in KEY=VALUE format (repeatable)") - clientContainerExecCmd.PersistentFlags(). + clientContainerDockerExecCmd.PersistentFlags(). String("working-dir", "", "Working directory inside the container") - _ = clientContainerExecCmd.MarkPersistentFlagRequired("id") - _ = clientContainerExecCmd.MarkPersistentFlagRequired("command") + _ = clientContainerDockerExecCmd.MarkPersistentFlagRequired("id") + _ = clientContainerDockerExecCmd.MarkPersistentFlagRequired("command") } diff --git a/cmd/client_container_inspect.go b/cmd/client_container_docker_inspect.go similarity index 88% rename from cmd/client_container_inspect.go rename to cmd/client_container_docker_inspect.go index 14abfcd2..ffcc2f92 100644 --- a/cmd/client_container_inspect.go +++ b/cmd/client_container_docker_inspect.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/internal/cli" ) -// clientContainerInspectCmd represents the clientContainerInspect command. -var clientContainerInspectCmd = &cobra.Command{ +// clientContainerDockerInspectCmd represents the clientContainerDockerInspect command. +var clientContainerDockerInspectCmd = &cobra.Command{ Use: "inspect", Short: "Inspect a container", Long: `Retrieve detailed information about a specific container on the target node.`, @@ -39,7 +39,7 @@ var clientContainerInspectCmd = &cobra.Command{ host, _ := cmd.Flags().GetString("target") id, _ := cmd.Flags().GetString("id") - resp, err := sdkClient.Container.Inspect(ctx, host, id) + resp, err := sdkClient.Docker.Inspect(ctx, host, id) if err != nil { cli.HandleError(err, logger) return @@ -97,10 +97,10 @@ var clientContainerInspectCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerInspectCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerInspectCmd) - clientContainerInspectCmd.PersistentFlags(). + clientContainerDockerInspectCmd.PersistentFlags(). String("id", "", "Container ID to inspect (required)") - _ = clientContainerInspectCmd.MarkPersistentFlagRequired("id") + _ = clientContainerDockerInspectCmd.MarkPersistentFlagRequired("id") } diff --git a/cmd/client_container_list.go b/cmd/client_container_docker_list.go similarity index 85% rename from cmd/client_container_list.go rename to cmd/client_container_docker_list.go index 3bbc55e1..f04346a4 100644 --- a/cmd/client_container_list.go +++ b/cmd/client_container_docker_list.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerListCmd represents the clientContainerList command. -var clientContainerListCmd = &cobra.Command{ +// clientContainerDockerListCmd represents the clientContainerDockerList command. +var clientContainerDockerListCmd = &cobra.Command{ Use: "list", Short: "List containers on target node", Long: `List containers on the target node, optionally filtered by state.`, @@ -40,17 +40,17 @@ var clientContainerListCmd = &cobra.Command{ stateFlag, _ := cmd.Flags().GetString("state") limit, _ := cmd.Flags().GetInt("limit") - params := &gen.GetNodeContainerParams{} + params := &gen.GetNodeContainerDockerParams{} if stateFlag != "" { - state := gen.GetNodeContainerParamsState(stateFlag) + state := gen.GetNodeContainerDockerParamsState(stateFlag) params.State = &state } if limit > 0 { params.Limit = &limit } - resp, err := sdkClient.Container.List(ctx, host, params) + resp, err := sdkClient.Docker.List(ctx, host, params) if err != nil { cli.HandleError(err, logger) return @@ -98,10 +98,10 @@ var clientContainerListCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerListCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerListCmd) - clientContainerListCmd.PersistentFlags(). + clientContainerDockerListCmd.PersistentFlags(). String("state", "running", "Filter by state: running, stopped, all") - clientContainerListCmd.PersistentFlags(). + clientContainerDockerListCmd.PersistentFlags(). Int("limit", 0, "Maximum number of containers to return") } diff --git a/cmd/client_container_pull.go b/cmd/client_container_docker_pull.go similarity index 83% rename from cmd/client_container_pull.go rename to cmd/client_container_docker_pull.go index 0173c56e..e610078c 100644 --- a/cmd/client_container_pull.go +++ b/cmd/client_container_docker_pull.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerPullCmd represents the clientContainerPull command. -var clientContainerPullCmd = &cobra.Command{ +// clientContainerDockerPullCmd represents the clientContainerDockerPull command. +var clientContainerDockerPullCmd = &cobra.Command{ Use: "pull", Short: "Pull a container image", Long: `Pull a container image on the target node.`, @@ -39,11 +39,11 @@ var clientContainerPullCmd = &cobra.Command{ host, _ := cmd.Flags().GetString("target") image, _ := cmd.Flags().GetString("image") - body := gen.ContainerPullRequest{ + body := gen.DockerPullRequest{ Image: image, } - resp, err := sdkClient.Container.Pull(ctx, host, body) + resp, err := sdkClient.Docker.Pull(ctx, host, body) if err != nil { cli.HandleError(err, logger) return @@ -73,10 +73,10 @@ var clientContainerPullCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerPullCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerPullCmd) - clientContainerPullCmd.PersistentFlags(). + clientContainerDockerPullCmd.PersistentFlags(). String("image", "", "Image reference to pull (required)") - _ = clientContainerPullCmd.MarkPersistentFlagRequired("image") + _ = clientContainerDockerPullCmd.MarkPersistentFlagRequired("image") } diff --git a/cmd/client_container_remove.go b/cmd/client_container_docker_remove.go similarity index 81% rename from cmd/client_container_remove.go rename to cmd/client_container_docker_remove.go index 21bfce7c..dcbfb97f 100644 --- a/cmd/client_container_remove.go +++ b/cmd/client_container_docker_remove.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerRemoveCmd represents the clientContainerRemove command. -var clientContainerRemoveCmd = &cobra.Command{ +// clientContainerDockerRemoveCmd represents the clientContainerDockerRemove command. +var clientContainerDockerRemoveCmd = &cobra.Command{ Use: "remove", Short: "Remove a container", Long: `Remove a container from the target node.`, @@ -40,12 +40,12 @@ var clientContainerRemoveCmd = &cobra.Command{ id, _ := cmd.Flags().GetString("id") force, _ := cmd.Flags().GetBool("force") - params := &gen.DeleteNodeContainerByIDParams{} + params := &gen.DeleteNodeContainerDockerByIDParams{} if force { params.Force = &force } - resp, err := sdkClient.Container.Remove(ctx, host, id, params) + resp, err := sdkClient.Docker.Remove(ctx, host, id, params) if err != nil { cli.HandleError(err, logger) return @@ -74,12 +74,12 @@ var clientContainerRemoveCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerRemoveCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerRemoveCmd) - clientContainerRemoveCmd.PersistentFlags(). + clientContainerDockerRemoveCmd.PersistentFlags(). String("id", "", "Container ID to remove (required)") - clientContainerRemoveCmd.PersistentFlags(). + clientContainerDockerRemoveCmd.PersistentFlags(). Bool("force", false, "Force removal of a running container") - _ = clientContainerRemoveCmd.MarkPersistentFlagRequired("id") + _ = clientContainerDockerRemoveCmd.MarkPersistentFlagRequired("id") } diff --git a/cmd/client_container_start.go b/cmd/client_container_docker_start.go similarity index 84% rename from cmd/client_container_start.go rename to cmd/client_container_docker_start.go index fe518089..4037cead 100644 --- a/cmd/client_container_start.go +++ b/cmd/client_container_docker_start.go @@ -28,8 +28,8 @@ import ( "github.com/retr0h/osapi/internal/cli" ) -// clientContainerStartCmd represents the clientContainerStart command. -var clientContainerStartCmd = &cobra.Command{ +// clientContainerDockerStartCmd represents the clientContainerDockerStart command. +var clientContainerDockerStartCmd = &cobra.Command{ Use: "start", Short: "Start a stopped container", Long: `Start a stopped container on the target node.`, @@ -38,7 +38,7 @@ var clientContainerStartCmd = &cobra.Command{ host, _ := cmd.Flags().GetString("target") id, _ := cmd.Flags().GetString("id") - resp, err := sdkClient.Container.Start(ctx, host, id) + resp, err := sdkClient.Docker.Start(ctx, host, id) if err != nil { cli.HandleError(err, logger) return @@ -67,10 +67,10 @@ var clientContainerStartCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerStartCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerStartCmd) - clientContainerStartCmd.PersistentFlags(). + clientContainerDockerStartCmd.PersistentFlags(). String("id", "", "Container ID to start (required)") - _ = clientContainerStartCmd.MarkPersistentFlagRequired("id") + _ = clientContainerDockerStartCmd.MarkPersistentFlagRequired("id") } diff --git a/cmd/client_container_stop.go b/cmd/client_container_docker_stop.go similarity index 82% rename from cmd/client_container_stop.go rename to cmd/client_container_docker_stop.go index bb744b57..7cebcbbd 100644 --- a/cmd/client_container_stop.go +++ b/cmd/client_container_docker_stop.go @@ -29,8 +29,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// clientContainerStopCmd represents the clientContainerStop command. -var clientContainerStopCmd = &cobra.Command{ +// clientContainerDockerStopCmd represents the clientContainerDockerStop command. +var clientContainerDockerStopCmd = &cobra.Command{ Use: "stop", Short: "Stop a running container", Long: `Stop a running container on the target node.`, @@ -40,12 +40,12 @@ var clientContainerStopCmd = &cobra.Command{ id, _ := cmd.Flags().GetString("id") timeout, _ := cmd.Flags().GetInt("timeout") - body := gen.ContainerStopRequest{} + body := gen.DockerStopRequest{} if cmd.Flags().Changed("timeout") { body.Timeout = &timeout } - resp, err := sdkClient.Container.Stop(ctx, host, id, body) + resp, err := sdkClient.Docker.Stop(ctx, host, id, body) if err != nil { cli.HandleError(err, logger) return @@ -74,12 +74,12 @@ var clientContainerStopCmd = &cobra.Command{ } func init() { - clientContainerCmd.AddCommand(clientContainerStopCmd) + clientContainerDockerCmd.AddCommand(clientContainerDockerStopCmd) - clientContainerStopCmd.PersistentFlags(). + clientContainerDockerStopCmd.PersistentFlags(). String("id", "", "Container ID to stop (required)") - clientContainerStopCmd.PersistentFlags(). + clientContainerDockerStopCmd.PersistentFlags(). Int("timeout", 10, "Seconds to wait before killing the container") - _ = clientContainerStopCmd.MarkPersistentFlagRequired("id") + _ = clientContainerDockerStopCmd.MarkPersistentFlagRequired("id") } diff --git a/cmd/provider_run.go b/cmd/provider_run.go deleted file mode 100644 index 08129fcf..00000000 --- a/cmd/provider_run.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "os" - - "github.com/spf13/cobra" -) - -var providerRunData string - -var providerRunCmd = &cobra.Command{ - Use: "run [provider] [operation]", - Short: "Run a provider operation (internal)", - Hidden: true, - Args: cobra.ExactArgs(2), - RunE: func( - _ *cobra.Command, - args []string, - ) error { - providerName := args[0] - operationName := args[1] - - reg := buildProviderRegistry() - spec, ok := reg.Lookup(providerName, operationName) - if !ok { - return fmt.Errorf("unknown provider/operation: %s/%s", providerName, operationName) - } - - var params any - if spec.NewParams != nil { - params = spec.NewParams() - } - if providerRunData != "" && params != nil { - if err := json.Unmarshal([]byte(providerRunData), params); err != nil { - return fmt.Errorf("parse input data: %w", err) - } - } - - result, err := spec.Run(context.Background(), params) - if err != nil { - return err - } - - output, err := json.Marshal(result) - if err != nil { - return fmt.Errorf("marshal result: %w", err) - } - - _, _ = fmt.Fprintln(os.Stdout, string(output)) - - return nil - }, -} - -var providerCmd = &cobra.Command{ - Use: "provider", - Short: "Provider operations (internal)", - Hidden: true, -} - -func init() { - providerRunCmd.Flags().StringVar(&providerRunData, "data", "", "JSON input data") - providerCmd.AddCommand(providerRunCmd) - rootCmd.AddCommand(providerCmd) -} diff --git a/cmd/provider_run_registry.go b/cmd/provider_run_registry.go deleted file mode 100644 index 8d5a57b1..00000000 --- a/cmd/provider_run_registry.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package cmd - -import ( - "context" - "io" - "log/slog" - - "github.com/retr0h/osapi/internal/exec" - "github.com/retr0h/osapi/internal/provider/command" - "github.com/retr0h/osapi/internal/provider/network/dns" - "github.com/retr0h/osapi/internal/provider/network/ping" - "github.com/retr0h/osapi/internal/provider/node/disk" - nodeHost "github.com/retr0h/osapi/internal/provider/node/host" - "github.com/retr0h/osapi/internal/provider/node/load" - "github.com/retr0h/osapi/internal/provider/node/mem" - "github.com/retr0h/osapi/internal/provider/registry" - "github.com/retr0h/osapi/pkg/sdk/platform" -) - -// PingParams holds the input for the ping.do operation. -type PingParams struct { - Address string `json:"address"` -} - -// DNSGetParams holds the input for dns.get-resolv-conf. -type DNSGetParams struct { - InterfaceName string `json:"interface_name"` -} - -// DNSUpdateParams holds the input for dns.update-resolv-conf. -type DNSUpdateParams struct { - Servers []string `json:"servers"` - SearchDomains []string `json:"search_domains"` - InterfaceName string `json:"interface_name"` -} - -// buildProviderRegistry creates a registry with all known provider operations, -// using platform detection to select the correct provider variant (Ubuntu, -// Darwin, or generic Linux). -func buildProviderRegistry() *registry.Registry { - reg := registry.New() - - logger := slog.New(slog.NewTextHandler(io.Discard, nil)) - execManager := exec.New(logger) - plat := platform.Detect() - - registerHostProvider(reg, plat) - registerDiskProvider(reg, plat, logger) - registerMemProvider(reg, plat) - registerLoadProvider(reg, plat) - registerPingProvider(reg, plat) - registerDNSProvider(reg, plat, logger, execManager) - registerCommandProvider(reg, logger, execManager) - - return reg -} - -func registerHostProvider( - reg *registry.Registry, - platform string, -) { - var p nodeHost.Provider - switch platform { - case "ubuntu": - p = nodeHost.NewUbuntuProvider() - case "darwin": - p = nodeHost.NewDarwinProvider() - default: - p = nodeHost.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "host", - Operations: map[string]registry.OperationSpec{ - "get-hostname": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetHostname() - }, - }, - "get-uptime": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetUptime() - }, - }, - "get-os-info": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetOSInfo() - }, - }, - "get-architecture": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetArchitecture() - }, - }, - "get-kernel-version": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetKernelVersion() - }, - }, - "get-fqdn": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetFQDN() - }, - }, - "get-cpu-count": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetCPUCount() - }, - }, - "get-service-manager": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetServiceManager() - }, - }, - "get-package-manager": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetPackageManager() - }, - }, - }, - }) -} - -func registerDiskProvider( - reg *registry.Registry, - platform string, - logger *slog.Logger, -) { - var p disk.Provider - switch platform { - case "ubuntu": - p = disk.NewUbuntuProvider(logger) - case "darwin": - p = disk.NewDarwinProvider(logger) - default: - p = disk.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "disk", - Operations: map[string]registry.OperationSpec{ - "get-local-usage-stats": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetLocalUsageStats() - }, - }, - }, - }) -} - -func registerMemProvider( - reg *registry.Registry, - platform string, -) { - var p mem.Provider - switch platform { - case "ubuntu": - p = mem.NewUbuntuProvider() - case "darwin": - p = mem.NewDarwinProvider() - default: - p = mem.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "mem", - Operations: map[string]registry.OperationSpec{ - "get-stats": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetStats() - }, - }, - }, - }) -} - -func registerLoadProvider( - reg *registry.Registry, - platform string, -) { - var p load.Provider - switch platform { - case "ubuntu": - p = load.NewUbuntuProvider() - case "darwin": - p = load.NewDarwinProvider() - default: - p = load.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "load", - Operations: map[string]registry.OperationSpec{ - "get-average-stats": { - Run: func(_ context.Context, _ any) (any, error) { - return p.GetAverageStats() - }, - }, - }, - }) -} - -func registerPingProvider( - reg *registry.Registry, - platform string, -) { - var p ping.Provider - switch platform { - case "ubuntu": - p = ping.NewUbuntuProvider() - case "darwin": - p = ping.NewDarwinProvider() - default: - p = ping.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "ping", - Operations: map[string]registry.OperationSpec{ - "do": { - NewParams: func() any { return &PingParams{} }, - Run: func(_ context.Context, params any) (any, error) { - pp := params.(*PingParams) - return p.Do(pp.Address) - }, - }, - }, - }) -} - -func registerDNSProvider( - reg *registry.Registry, - platform string, - logger *slog.Logger, - em exec.Manager, -) { - var p dns.Provider - switch platform { - case "ubuntu": - p = dns.NewUbuntuProvider(logger, em) - case "darwin": - p = dns.NewDarwinProvider(logger, em) - default: - p = dns.NewLinuxProvider() - } - - reg.Register(registry.Registration{ - Name: "dns", - Operations: map[string]registry.OperationSpec{ - "get-resolv-conf": { - NewParams: func() any { return &DNSGetParams{} }, - Run: func(_ context.Context, params any) (any, error) { - pp := params.(*DNSGetParams) - return p.GetResolvConfByInterface(pp.InterfaceName) - }, - }, - "update-resolv-conf": { - NewParams: func() any { return &DNSUpdateParams{} }, - Run: func(_ context.Context, params any) (any, error) { - pp := params.(*DNSUpdateParams) - return p.UpdateResolvConfByInterface( - pp.Servers, - pp.SearchDomains, - pp.InterfaceName, - ) - }, - }, - }, - }) -} - -func registerCommandProvider( - reg *registry.Registry, - logger *slog.Logger, - em exec.Manager, -) { - p := command.New(logger, em) - - reg.Register(registry.Registration{ - Name: "command", - Operations: map[string]registry.OperationSpec{ - "exec": { - NewParams: func() any { return &command.ExecParams{} }, - Run: func(_ context.Context, params any) (any, error) { - pp := params.(*command.ExecParams) - return p.Exec(*pp) - }, - }, - "shell": { - NewParams: func() any { return &command.ShellParams{} }, - Run: func(_ context.Context, params any) (any, error) { - pp := params.(*command.ShellParams) - return p.Shell(*pp) - }, - }, - }, - }) -} diff --git a/docs/docs/gen/api/delete-node-container-by-id.api.mdx b/docs/docs/gen/api/delete-node-container-docker-by-id.api.mdx similarity index 93% rename from docs/docs/gen/api/delete-node-container-by-id.api.mdx rename to docs/docs/gen/api/delete-node-container-docker-by-id.api.mdx index 43c96215..a6bcc82b 100644 --- a/docs/docs/gen/api/delete-node-container-by-id.api.mdx +++ b/docs/docs/gen/api/delete-node-container-docker-by-id.api.mdx @@ -1,11 +1,11 @@ --- -id: delete-node-container-by-id +id: delete-node-container-docker-by-id title: "Remove a container" description: "Remove a container from the target node. Use the force query parameter to remove a running container." sidebar_label: "Remove a container" hide_title: true hide_table_of_contents: true -api: eJztWFFv2zYQ/isHYg/JIDtK53SrgT24TQJ4aIuiTVFgmWfQ4tliIpEqeUrjGvrvw1GWrMTOmnbt0AJ5sizxjnffd3c83koo9InTBWlrxFC8xtxeIUhIrCGpDTqYO5sDpQgk3QIJjFXYh7cew8u5dQnC+xLdEgrpZI6EDsiCazS50hhtFhuN/b+MiATJhRfDc/GseT19IY1cYI6GpqNX42m7fmoLdJIN9GISifbfWImhOMYMCV9aha2ip8vxsYiEx6R0mpZieL4ST1E6dKOSUt6zVT384DShmFSTSLTW+yBhZI5iKFLrKTxGQjNAhaRURMLh+1I7VGJIrsToFopnNVJygYag0RCBQ4/uChU4WxJDciWzEmFvKs0ygqnMsv0IrINMzjADjxkmZB3sXeJyGJbu19Bd96wsdC+xChdoenhNTvZqPFfiSmZaSWLbGyOjXJvfD6PwZVqzKKpI+CTFXLIMLQte78lpsxCRyLV5jmbBYB1WVdSCodVnwdAyAlqhIT3X6GCPdbGX4+P9PrwoPYEn6Qg+aEpBGpBZkUpT5uh0AkkqnUw4pKRRkMtlE0aQITFXESi90OQjKI1C5xPrkF9afsUy6bJI0fj/gtz9weIoYrPY+7/PZe/jqPdn3Hsy2TxO+73Jzz+JLqohhRpgQybdQHYuM78F7WlIu5BjMgM7vzvP7uuyzTVhXtByp7czazOUrE/hXJYZre2qOHMc+sIaj0Hlo/gR/9wVCY3JMkmwIFR9EYV0REMsJosi00lI74MLz7KrbWPs7AITEpEoHBcD0vXOF3Y21WoXRXPrckliKMoyhPCtbE0RLuwMxsdQelRcvApnE/QeKNUemAr0xJbitcyLjHUfHcX42yCOe/joyaw3OFSDnvz18HFvMHj8+OhoMIjjOGYkHfoyI9+xSjonmWGG2+/y6nZFZgU1xZuinOk5JsskQ5AJL+xvodEWrh14bPvfrOZ9uKyH0tVnB3Yj+ulED8I5ei8X97DhDUkqPazX34R643UoFKhYc5JKs0B1R4h2Vb9LkVI+k1KE9uyA3Co2U4FfesKcVRMGm9E56z5t8QkvawwG3YEN5lJnHNlV1c3j8w0lk0iQpuBcC90o8FizPSbMRXVbvAmlu6Wf2YzPjFpPSEnWUkViEMfbSTk2If2b+N4c3/4rJuU90RxB538ThEEWKJUENklK5+pysYmM0wB03W2Q03iFgcfSByIVktSZv8fmSml+lBmsZUDObEkbI3Zuq0rkrQ3SB+sugXSOtqyThqttZ19tCBfodmZe7SQL3NjkKI6ZuIboEG1bpB5uk/rWyJJS6/RHVNCD0asxXOIS2jh6IPZHIPaXbWJPrZtppdBAD8bGl/O5TjRXmwJdrr0P7fEDuz8Cu4N/a5CMJZjb0nzNzuiBzW/G5tGuk7XuDUKre7Mjf6D0u6c0dK2UWh4uqDBcEFF93R2KA559HKyaPq46aJk9WGlVhaGDu6onCJPNBOINc1rT1p1DtG6kRIVYX7pCJxsWiWj9cNrcXv54dxZ6Sm3mNoivnRiFrnMzPuFTX0SCDanxOOzH/XAbKaynXIZAW18+twc+t9FcbUL2/xwP1dAQXtNBkUlt2PzSZWxPzca54K1EJIadGU3Xi6FW3CvzV169Ws2kx7cuqyp+XV+0edKjtJez7K6rdtf9+9+6d5p/icvOdT9MdMRQCL5CX0mn2YjPNGjv9bqv24dvPHPa6VBzozXLrj+Noy0tPOv4Ip++vwHSF8CglagmVSRSlApdILj+MAojkI7I1nnAkdEWo+OT5ydnJyIS8mb1uFUtwgY7LVqt6hVn9hJNVbUGEv9nG6vqH5hFq3Q= +api: eJztWFFv20YM/ivEYQ/JIDtO53SrgT24TQp4WIeiTVFgmRfQOtq6RLpT76g0rqH/PvBk2U7srGnXDi2QJ0vykUd+H8njcaE0hdSbko2zaqBeUeGuCBBSZxmNJQ9T7wrgjIDRz4jBOk1deBMofpw6nxK8q8jPoUSPBTF5YAe+1eQra42drTV2/7IqUYyzoAZn6till+TPX6DFGRVk+Xz4cnSum6+uJI9iWlDjRK3eRloN1DHlxPSH0/Ss1dzoejofHatEBUorb3iuBmcL9ZTQkx9WnMmejfbBe2+Y1LgeJ2pleojLLRakBipzgeNjooygUyJnKlGe3lXGk1YD9hUltyA8bWDCGVmGVkMCngL5K9LgXcWCxxXmFcHeOdp5AueY5/sJOA85TiiHQDml7DzsXdJ8EJfuN7hddxyWppM6TTOyHbpmj50GzIW6wtxoZLG9NTIpjP31MIn/nDcUqjpRIc2oQJHheSnrA3tjZypRhbG/k50JUod1nazAMPqTYFiRAkaTZTM15GFPdImXo+P9LryoAkNg9AzvDWeAFjAvM7RVQd6kkGboMZV4QquhwHkbQ5ATC1cJaDMzHBKorCYfUudJPjr5JDLZvMzIhv+C3P3BkigSs8T7v8+w82HY+bPXeTJeP553O+Mff1CbqMb8aYGNaXQD2SnmYQva5zHnYoJhDm56d5Ld12VXGKai5PlObyfO5YSiT9MUq5yXdtWSOZ5C6WygqPJR75H83BUJrcmYplQy6a5KlFhMlkUMyzI3aczwg4sgsottY9zkglJWiSq91AM2zc4XbnJu9C6Kps4XyGqgqiqG8K1szQgu3ARGx1AF0lK5Su9SCgE4MwGECgosltI1FmUuuo+OevRLv9fr0KMnk07/UPc7+PPh406///jx0VG/3+v1eoKkp1DlHDasQu9RGBa4wy6vbpdjUdBQvK7IuZlSOk9zAkxlYXcLjVXh2oHHtv/tatlHanosXV1xYDeiH0/0KFxQCDi7hw2vGbkKsFx/E+q117FQkBbNaYZ2RvqOEN1U/TYjzuRAyghWxwcUTouZGsI8MBWiminaTN47/3GLT2RZazCYDdhgiiaXyK7rzTw+W1MyThQbjs4159UwkthQPWIqVH1bto2jO0SfuVxOi0ZJTEZRUSeq3+ttp+PIxsRvI3t9aocvmI73xHEIG+9t+EVZ4AwZXJpW3jeFYh0TzyPETZPB3tAVRQarECnUxGjycI/NtTbyiDksZQAnruK1ETu31RXJ1pb4vfOXwKYgVzXpInV2Y19jmWbkd+Zc46QI3NjkqNcT4lqWY5xtkXq4TeobixVnzpsPpKEDw5cjuKQ5rILogdjvgdiftol97vzEaE0WOjCyoZpOTWqkzpTkCxNC7I0f2P0e2O3/W2tkHcPUVfZL9kQPbH41No92naxNVxCb3Ju9+AOl3zylsV/lzMlkQcfJgkqai+5AHcjI42DRdnD1wYrZg2aScLAwuo4jB3/VjBDG6/nDa6G2YW9zCrHyJmMu1fLWFVvZuEgly4fn7fXlt7ensak0duqi+NKXYWw71/MTOfxVosSQBpbDbq8bryOlC1xgjLfl7XN73HMb1MU6cv/P4VADDdM1H5Q5GivmVz4XexpSzpRspRI12BjS3PAiMiMLjJbGWZaJ2GIxwUBvfF7X8rm5csvMR5uAk/yuS/cmDve/f+/045LmGxf/ONtRA6XkMn2F3ogRn2jQ3qtln7cPX3n6tNOh9m5r55v+tI6u+JGpx2f59O2Nkj4DBqNVPa4TlRFq8pHg5o9hHIZsiGydDxIZq+J0fPL7yemJShTeLCO3ykbcYKdFi0Wz4tRdkq3rlYEs72JjXf8DKoqs5A== sidebar_class_name: "delete api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/container-management-api-container-exec.tag.mdx b/docs/docs/gen/api/docker-management-api-docker-exec.tag.mdx similarity index 72% rename from docs/docs/gen/api/container-management-api-container-exec.tag.mdx rename to docs/docs/gen/api/docker-management-api-docker-exec.tag.mdx index d04bbf28..7a0e24fc 100644 --- a/docs/docs/gen/api/container-management-api-container-exec.tag.mdx +++ b/docs/docs/gen/api/docker-management-api-docker-exec.tag.mdx @@ -1,7 +1,7 @@ --- -id: container-management-api-container-exec -title: "Node/Container/Exec" -description: "Node/Container/Exec" +id: docker-management-api-docker-exec +title: "Node/Docker/Exec" +description: "Node/Docker/Exec" custom_edit_url: null --- diff --git a/docs/docs/gen/api/container-management-api-container-operations.tag.mdx b/docs/docs/gen/api/docker-management-api-docker-image.tag.mdx similarity index 60% rename from docs/docs/gen/api/container-management-api-container-operations.tag.mdx rename to docs/docs/gen/api/docker-management-api-docker-image.tag.mdx index d52cc96d..ddf9911f 100644 --- a/docs/docs/gen/api/container-management-api-container-operations.tag.mdx +++ b/docs/docs/gen/api/docker-management-api-docker-image.tag.mdx @@ -1,13 +1,13 @@ --- -id: container-management-api-container-operations -title: "Node/Container" -description: "Node/Container" +id: docker-management-api-docker-image +title: "Node/Docker/Image" +description: "Node/Docker/Image" custom_edit_url: null --- -Container lifecycle operations on a target node. +Docker image operations on a target node. diff --git a/docs/docs/gen/api/container-management-api-container-image.tag.mdx b/docs/docs/gen/api/docker-management-api-docker-operations.tag.mdx similarity index 60% rename from docs/docs/gen/api/container-management-api-container-image.tag.mdx rename to docs/docs/gen/api/docker-management-api-docker-operations.tag.mdx index d67c27ac..33b16ebd 100644 --- a/docs/docs/gen/api/container-management-api-container-image.tag.mdx +++ b/docs/docs/gen/api/docker-management-api-docker-operations.tag.mdx @@ -1,13 +1,13 @@ --- -id: container-management-api-container-image -title: "Node/Container/Image" -description: "Node/Container/Image" +id: docker-management-api-docker-operations +title: "Node/Docker" +description: "Node/Docker" custom_edit_url: null --- -Container image operations on a target node. +Docker lifecycle operations on a target node. diff --git a/docs/docs/gen/api/get-node-container-by-id.api.mdx b/docs/docs/gen/api/get-node-container-docker-by-id.api.mdx similarity index 93% rename from docs/docs/gen/api/get-node-container-by-id.api.mdx rename to docs/docs/gen/api/get-node-container-docker-by-id.api.mdx index f1d73908..bf25e7eb 100644 --- a/docs/docs/gen/api/get-node-container-by-id.api.mdx +++ b/docs/docs/gen/api/get-node-container-docker-by-id.api.mdx @@ -1,11 +1,11 @@ --- -id: get-node-container-by-id +id: get-node-container-docker-by-id title: "Inspect a container" description: "Get detailed information about a specific container." sidebar_label: "Inspect a container" hide_title: true hide_table_of_contents: true -api: eJztWF9v2zYQ/yoEsYdkkGw5tbNUwB7Spu08rEXQpSvQzDNo8WwxkUiVPLlRDX334ShbtmO3SdsNaIE8WZbu//3ujrwFl+ASqwpURvOYvwBkElCoDCRTempsLugTExNTIhPMFZCoqUpYYjQKpcF2/tY84ChmjseX/Onq9fil0GIGOWgcn54Pxy392BRgvVDHRwFv/w1lo/+VkdBKeVINz3jAHSSlVVjx+HLBn4CwYE9LTElhKze2ICQf1aOAF8KKHBCs8wxa5MBjnhqH/jHginwtBKY84Bbel8qC5DHaEoJbAbkQdgbIxAw0spWEgFlwYOcgmTUlKj1jc5GVwA7GQlcBG4ssOwyYsSwTE8iYgwwSNJYdXEMVe9LDJmw3oRGFChMjYQY6hBu0ImxiueBzkSkpkGxfGRnkSv/aC/yXMXrbeB1wl6SQC+LBqiB6h1bpGQ94rvQfoGcUq15dB20wlPyiMLQJYUqCRjVVYNkBySIvh2eHHfaydMgcCovsg8KUCc1EVqRClzlYAkwqrEgQLBNaslxUKwixDJByFTCpZgpdwEotwbrEWKCXhl4RT1oVKWj3LZG7f7AIRWQWef/PpQg/nobvovDxaP047oSjn3/iNSHOgiuMduC1H0UR/Xwqgk19dXjgsQsaiVgURaYSXwjdK0cci11DzeQKEuQBLyyVDapG35WZjJXc51BTvzzmZekTfgvbKbArM2HDM1Y6kAwNK6xJwDmGqXKMQgcOyVK4EXmRkezBIIKTfhSFcPR4EvZ7sh+KX3rHYb9/fDwY9PtRFEUUZQuuzNBtWCWsFRWhDiF3+7zatu7sc21o3X12otGW+Z547Pq/omZmyjCFptA75MD+iN5dFtvhEr3JUfJI9mEwPSap97NsLZfotyXmVahnSt94G3Mxu4e4IZE1OZ5U3s2t+K1le8FxJhBc01fQF9Gd5pbWUn9spTLPuC3blloTcx3wxIJA+KL4ehZCAKocHIq82JZ+FB31w6gX9gYXvSh+FMVR9I5UFcbuBeG2nnNjkeWiKJSeuc4ejC7tqzdUXvKT6CSKTyI+qgOem1LfQ9FfJitzYA31vRV1pUARd+fCdjM18f+8UtDzOzU+03NljaY5zObCKjHJ4LOaCaSAH4y9HjtAGm73KNZXDQchYKpmZTPRSYuQUtGzyM63qnRXaQoio857FyZ+83QsSSG59jgrHVPTVjPIbWA0YisPu1To2RbsJsZkIPSOjrcpYArWl0p7QGG5kVThkrnKIeQrkFMirDX2btOfERnLwTmqR7XRcdjU97qOj8R6FF+uu9ko4KjQu9RWRdMiXy9nD69vM6968Kd5n5qMjibK6E0pdcD7+2bYUPvBuhoMbH3O+g+n2T1jeco2/q+6t+dlmApkJkmoK91Cw/NmpKBhFtAqmMMSQj6NzWTeg88d5S2ql9PcLSdTa8RetbIEUr0sL9/KTNlMGzrHbOhVGmEGdu/Iapwkhi0lgyiixK0S7bG2k9TeblLfaFFiaqz6CJKF7PR8yK6hYi2OHhL7IyT20W5inxs7UVKCZiEbaldOpypR1GsKsLlyzt/AHrL7I2S3/7n7hDbIpqbU8qEJ/wjZHOybrM3JQGlar/h9wtYB/SGp33dS6fwPmBraYNFGJmiWKjHvaiOhu1gd4upum9buQsnab7bsvNlTjdZrrj8poU3ONpddrQ8pYsGXiwx/jPVEPFg+PF/d+n9/e+EPlHSD9uxLD079kXO9oKOhzwNOhjTB6HWiTtRcnxzmwqNsuTgaNhjdvIPfjuViDdhvWCg2riLcYLfIhNJkTmkzEt9E95JTdHnA443N3qZRsZJ09KWvRL1YTISDNzara3r9vgRbNXFf3Yv8tlAqR8+Sx1ORudubsE3fDl4vz0iH7H9eE+4Nxurap+nS56l5zHnAr6Ha3HbS0u+rfPr+dn5fEQYleT1qbpcSrE9w8+E0SaDADZadzkp7vbasXzy7oNvsdh3eqjsvfa85i0VDcWGuQdd1ax3SfzKwrv8FFJgoYA== +api: eJztWG1v2zYQ/isEsQ/JINlyamepgH1Im7bzsBZBl65AM8+gxbPFRCJV8uTGNfTfhyP9GrtN2m5AC+STJfl4L8+98uZcgsusqlAZzVP+ApBJQKEKkEzpsbGloL+YGJkamWCugkyNVcYyo1EoDbb1t+YRRzFxPL3kZya7Bjt8KbSYQAkah6fn/aEMX00F1rNzfBDx1VtfBsmvjISnS7aB0ZNZ/4xH3EFWW4Uznl7O+RMQFuxpjTkJDKxTC0LyQTOIeCWsKAHBOk+tRQk85blx6B8jrsjQSmDOI27hfa0sSJ6irSG6hcaFsBNAJiagkS05RMyCAzsFyaypUekJm4qiBnYwFHoWsaEoisOIGcsKMYKCOSggQ2PZwTXMUk96GDC7iY2oVJwZCRPQMdygFXEAcs6nolBSIOm+VDIqlf61E/l/huh1403EXZZDKegMziqid2iVnvCIl0r/AXpCQHWaJlqBoeQXwbDyCVMSNKqxAssOiBdZ2T87bLGXtUPmUFhkHxTmTGgmiioXui7BUrTkwooMwTKhJSvFbBk/rAAkX0VMqolCF7FaS7AuMxboo6FPdCafVTlo9y3I3R8siiJSi6z/51LEH0/jd0n8eLB+HLbiwc8/8YYizoKrjHbgpR8lCf18CsGQXC0ecQIANBKxqKpCZT4X2leOTsx3FTWjK8iQR7yylDmogrwrMxoquc+gkLw85XXtHX4rtnNgV2bE+mesdiAZGlZZk4FzDHPlGEEHDklTuBFlVRDvXi+Bk26SxHD0eBR3O7Ibi186x3G3e3zc63W7SZIkhLIFVxfoNrQS1ooZRR1C6fZZta3d2edq0Lr07KCxSvM9eOzav6RmZswwh5DoLTJgP6J3p8U2XKIzOsoeyS70xsfE9X6arfkS/TbHchbridI3XsdSTO7Brk9kwcejmTdzC781b884LQSCC3UFfRLdqW5tLdXHFVfmD27ztrXWdLiJeGZBIHwRvv4IRQCqEhyKstrmfpQcdeOkE3d6F50kfZSkSfKORFXG7g3CbTnnxiIrRVUpPXGtPTG60K/ZEHnJT5KTJD1J+KCJeGlqfQ9Bf5miLoEF6nsLakuBIm1PhW0XauTfvFDQ0zslPtNTZY2mPsymwioxKuCzkilIAT8Yez10gNTc7pGsr8IJioCxmtShqZMUIaWiZ1Gcb2XprtAcREGV966Y+M3TsSyH7NrHWe2YGq8kg9wOjMB25sMuF3qyFXYjYwoQekfG2xwwB+tTZTWjsNJIynDJ3MwhlMsgJ0dYa+zdqj8jMlaCc5SPaqPisLGvdS2PxLoVX66r2SDiqNCbFOaiUB9fLxoPb26fXBbgTxx8agoaSpTRmyyaiHf3da++9i112RLYesL6D/vYPVE8ZRvvy7rtzzLMBTKTZVSPbsXB89BM0DALaBVMYRE83oGhJ++JzB3hq3he9HG36EkrJfaKlTWQ6EVi+SJm6tBnaILZkKs0wgTs3mYVjKQDW0J6SUKOW3rZR9mOUzu7Tn2jRY25seojSBaz0/M+u4YZWwXRg2N/BMc+2nXsc2NHSkrQLGZ97erxWGWKqkwFtlTO+evXg3d/BO92P3eT0AbZ2NRaPhThH8GbvX2dNcwEStNWxW8StkbzB6d+306lyR8wN7S+ol1MFNYpKW9rI6E9X45vTXvl1nZYV7XnSjZ+r2WnYVE1WC+5/iS/BtdtrrpWpuSIFV9sMvwc64l4tHh4vrz2//72wk+UdIX2xxeGnPqZc72ho97PI06KBEw6raSVhPuTw1L4YFtsjvohVDcv4bchna/j9hvWicFUhBtsV4VQmtSpbUHsA8iXnEDmEU83VntbSnmkiUBJmoOJjI7N5yPh4I0tmoY+v6/BzoIDljckvzeUytGz5OlYFO72TmzTyIPXi5npkP3PC8O9qCwvgJquf56ap5xH/Bpmm3tPWv99lU3f3/bvK2BQkjeDcM+UYL2Dwx+nWQYVbhzZqbS04Vul+YtnF3Sv3U7IWwnoue9VZz4PFBfmGnTTrLRDeicFm+Zfd4Mp0A== sidebar_class_name: "get api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/get-node-container.api.mdx b/docs/docs/gen/api/get-node-container-docker.api.mdx similarity index 93% rename from docs/docs/gen/api/get-node-container.api.mdx rename to docs/docs/gen/api/get-node-container-docker.api.mdx index 257fd8f5..a95cc55b 100644 --- a/docs/docs/gen/api/get-node-container.api.mdx +++ b/docs/docs/gen/api/get-node-container-docker.api.mdx @@ -1,11 +1,11 @@ --- -id: get-node-container +id: get-node-container-docker title: "List containers" description: "List containers on the target node, optionally filtered by state." sidebar_label: "List containers" hide_title: true hide_table_of_contents: true -api: eJztWG1v2zYQ/isEP7UAncip3W0C+iF9SeGhG4o2RYGlhkGLZ4uJSKrkKYtm6L8PR1myHTttUrTABvRT7Ph4fO557o7krbiCkHldonaWp/yNDsgyZ1FqCz4wZxnmwFD6JSCzToFgLhrLoqjZQhcIHhSb1yygRDj6ZLngKJeBpxf8Redo9oe0cgkGLM5O305m/Q4zV4KX5C/wqeD9t4niKX8N+KdT0HvhggfIKq+x5unFij8H6cGfVpjTZr3P1INUfNpMBS+llwYQfIgLrDTAU567gPGj4JqCLiXmXHAPnyvtQfEUfQXiFjPnLQVyCRZZ50EwDwH8NSjmXYXaLtm1LCpgj2bS1oLNZFE8Fsx5Vsg5FCxAARk6zx5dQZ1G08ctZTcDJ0s9yJyCJdgB3KCXg5bHFb+WhVYSCXsHUhhtnw1F/GXWysMbwUOWg5G0BuuS7AN6bZdccKPtG7BL4mrYNKInI8rWMfG5Al/vULGQRdjj4izKvp0nvf7sJSxkVWBg6NgnLoviE39YhM5oBFNiLZwFt3jmK2uJ2ICuLEExWRRfjlS1CHhKu3PBwVaGEmTtiLKodcVFtJhu01Foo/GBdPwhb7SpDLOVmYNnbrHNDDrmAStvv5WFVmgjb54Nk+Rg5NoiLGN59KGfJFFxgsXToeCmhcjTYZI0VBkeQulsgLjvSZLQnwOtYCeWIy5ilYFFMpdlWegsluvxZaA1q31sbn4JGRFaeipu1O2Ol24+0+qQegvnjSTxqkorvleFObBLN2eTl6wKoIjd0rsMQmCY68BIKQhISOFGmrIg3+NxAr+OkmQAJ7/NB6OhGg3kL8Ong9Ho6dPxeDRKkpZYD4EydwuV9F5SApAW4VBUu+j6TsVCZYz0NVtQ6Uci+z53tEdG348OJvPt8Dtr0oZ6c+xIR4R/o9SBEL4mbtvqdej83Tvo517DlqMu9P0wD+t9F4NagUW90OB3xZTD+Un2RI1gvHhKQd+PuI1fst/1aOqBXWp7Q960kct7uJuQWZuB8zqq0Me/6zs6TguJENr+HLvt1+FW3tM5s8Vq7K47vrt+Rsp7kAgP4jcu0SS6NhBQmnLX+0lyMhokw8FwfD5M0idJmiR/8aYRHDVGg97V+1bw+GOWS7vcATJ3rgBp95B8zAFz8JG8vjiYcYo0VyzUAcF0YTeCg/fOfz3AV2TGDIRACumtEmELqQtQRxHnpqNfbMpveiA4KpQJguHN7WVdt7hr1QtX0GGvnX237rXkoxF8dKjbTmxs/l0DY5uby3fsuvfk8JRtfe/aTFzLMJfIXJZRfqrdhDmL9K7PO6/hGqJ6VYjyKUCpi3CPzZXS7f2SrdcwOXcVbkAc3FZVQFtbwL+dv4pJ7aquLSq447S83VvbIGnBziZjOjQ3Mscc2xN1uC/qBysrzJ3X/4BiA3b6dsKuoGZ9Fv0U9v8g7JN9Yc+cn2ulwLIBm9hQLRY609RjSvBGhxDfMz/V/e+rOz7Ui9szhK5t9O74Ifffn5L+IEkbwQ1g7mh+QG9i0b7uU35Mw4vjVXfYN8fZzljBX7dDgulmxvCetGzl2p409PBzxJKvn2PxphONuFh/OOseMr9/PI93Dm0XLi5fgz+Nt5LNZITOBy44AWl5GB4lR/FhUrqARsYEWz9Tb81pblO42uTp9xrptDEj3OBxWUhtCVflC9qrZfiCky8ueLo1X9mQPBXxokV2q9VcBvjgi6ahf7evbJrPKB3kvLjrnb0d1DdNIA7GcAX11hQkjmR4yjmNBO4P5yETgC+g6IYPGxRT+uI1wXggQ4/erW8Zj9kPHl0dDKh7fdp6O54u0D5DmmkjeA5SgY/xtT+fZhmU20TstVoipq/z16/OaZSzW523qjF6PwhqtWotzt0V2KbpMSJ9J4BN8y9Es1Ve +api: eJztWG1v2zYQ/isEP7UAncipnW0C+iF9SeGhG4o2RYGlRkCLZ4uJRKrkKYtm6L8PR1rya9ukaIEN6Cdb0vF4z/Mcj+QtuQKfOV2htoan/LX2yDJrUGoDzjNrGObAULoFIDNWgWA2GMuiaNhcFwgOFJs1zKNEOPpouOAoF56nl/yFzW7AXf0hjVxACQavzt5MrlR8aytwkjx5PhW8f5oonvJXgH9aBc+7QKIjLriHrHYaG55eLvkzkA7cWY05TRbdpg6k4tN2KnglnSwBwflgbWQJPOW59Rj+Cq4JcSUx54I7+FRrB4qn6GoQO7RcRPxyAQZZ50EwBx7cLSjmbI3aLNitLGpgj66kaQS7kkXxWDDrWCFnUDAPBWRoHXt0A00aTB9Hvu4GVlZ6kFkFCzADuEMnB5HEJb+VhVYSKfYuSFFq83QowperqA1vBfdZDqWkMdhUZO/RabPggpfavAazIKKGbSt6MoJmHROfanDNFhVzWfg9Ls6D5ptJ0ovPXsBc1gV6hpZ95LIoPvKHIbSlRigrbIQ1YOdPXW0MEevRVhUoJoviy0hVjICnNDsXHExdUnasHFEKRVdcBIvpJh2FLjU+kI4/5J0u65KZupyBY3a+yQxa5gBrZ76VhSh0Ke+eDpPkIHJtEBZhbfTQT5KgOIXF06HgZQyRp8MkaWllOPCVNR7CvCdJQj8H6sAWliMuOD2BQTKXVVXoLKzY42tPY5b7sdnZNWREaOVofaOOM17b2ZVWh9SbW1dKEq+uteJ7qzAHdm1nbPKC1R4UsVs5m4H3DHPtGSkFHilSuJNlVZDv8TiBX0dJMoCT32aD0VCNBvKX4elgNDo9HY9HoySJxDrwlLkbUUnnJCUAaeEPodqOri9WzNdlKV3D5rT0A5F9qTvaI6OvRweTeRd+Z03aUGEOFemI4l8rdQDC18SNdV77zt+9QT9zGjYcddD3YR7W+3MMagUG9VyD2xZTDmcn2RM1gvH8lEDfj7i1X7Lf9lg2A7PQ5o686VIu7uFuQmYxA2dNUKHHv+07OE4LieBjfQ7V9uvh1s7RPrPBaqiuW767ekbKO5AID+I3DNEkui7Boyyrbe8nyclokAwHw/HFMEmfJGmS/MXbVnDUGAzihvwuqh2+ZLk0i60oZtYWIM1eGB9ywBxcYK5fGay0igRXzDceoewwt4KDc9Z9Hd1LMmMleE/y6I31weZSF6COQpzrcn65XnvTXWS0RCYIJW93x3R14uCQ57agPV5b83ZVYslBK/joUJGdmFDzu7rF1geW71hs78neGdt47qpLGMswl8hsllFaqu08OQ/ErrY5p+EWgm61D8IpQKkLf4/JldLxTMlWY5ic2RrXQRycVtVAUxvAv627Cbls664aKvjMJrlbUiNIGrA1yZj2yrXGIbv2RB3ui/reyBpz6/Q/oNiAnb2ZsBtoWJ9CP4X9Pwj7ZF/Yc+tmWikwbMAmxtfzuc40VZcKXKm9DzeZn+r+99UdH6rFcfeg0xpdN37IsfenpD9I0lbwEjC31Dmgq7CIl/qUH1PD4njZbfPtca/rsVp3FNxtbBFM1+2FdyRpVG2zydCjyBErvrqMhaNOMOJi9ee8u8b8/uEiHDq0mdswfIXhLBxL1n0R2ia44BRIpGN4lByFa0llPZYy5NnqkrrTotllcrlO1+/VzYmYEe7wuCqkNhRX7QqaKxJ9yckXFzzd6K7081KEke2pCEcuGrBczqSH965oW3odL9vUplHay1nxuev2JrpvakQcBHMDzUYzJHRmeMo5dQbuH85DGgFfiKLrQayjmNKD0xTGAxl69HZ16njMfnAH6yCg7hJqmk08HdA+VdppK3gOUoEL+OLnsyyDapOIvdJLxPTr/tXLC+robC/TnWUZvB8MarmMFhf2Bkzb9jEiPVOAbfsvu5lVgw== sidebar_class_name: "get api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/post-node-container-exec.api.mdx b/docs/docs/gen/api/post-node-container-docker-exec.api.mdx similarity index 93% rename from docs/docs/gen/api/post-node-container-exec.api.mdx rename to docs/docs/gen/api/post-node-container-docker-exec.api.mdx index 7f283b94..2e4fb8d1 100644 --- a/docs/docs/gen/api/post-node-container-exec.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker-exec.api.mdx @@ -1,11 +1,11 @@ --- -id: post-node-container-exec +id: post-node-container-docker-exec title: "Execute a command in a container" description: "Execute a command inside a running container on the target node." sidebar_label: "Execute a command in a container" hide_title: true hide_table_of_contents: true -api: eJztWG1v2zYQ/isEsQ/JIMdO53SrgX5w2xTI1m5Bmq7YMs84i2eLjUSq5Mmxa+i/D0dZ8muTrOuAFsg3WT4e757n7kQ+C6nQx07npK2RPXk6w7ggFCBim2VglNDGa8UvXGGMNhMRW0OgDTphjaAEBYGbIAljFR79ZWQkCSZe9q7k89py+BoMTDBDQ8P++dmwcTHEGcZyEEmbowOO4UzJnjy3nn61ChsHHJaMpMe4cJrmsne1kM8QHLp+QQnv1bjsYZWCHJSDSObgIENC58MaAxnKnkysp/AYSc1Z50CJjKTDD4V2qGSPXIHRFjSXVZowQUOi9hAJhx7dFJVwtiDGZwppgeJgCGYeiSGk6WEkrBMpjDAVHlOMyTpxcI3zXjA9rECbtSzkuhVbhRM0LZyRg1aF5EJOIdUKiGOvg4wybZ4eR+GfYUWBLCPp4wQz4DU0z9nek9NmIiOZafMKzYThOi7LqAFDq38FQ8OJ0AoN6bFGJw7YF2d59uLwSLwuPAlP4EjcaEoEGAFpnoApMnQ6FnECDmJCJ7jAMpjXNSVSJOYqEkpPNPlIFEah87F1yC8tv+I1yTxP0Pj/gtz9weIq4rA4+7+voPWx3/qz03oyWD0Oj1qD77+TJVcc74Kenlk1Z8dbNZRg01hkxbJU6x6j8O8S3qM9TPCfaIj9Qp6nOg4t037v2fliNx07eo8xyUjmjhuMNAZMlhGsGYJzMJe7VN87Uk2Y+V0gy0jiDLI8Re7R1MtItlKQkWxz038ucezVTO8Mv6+U5kdIBZqpdtbwABJTcBpGKXqhjfjl9I+nv/dfvT0VY+syoNtyKSN5Y921NpOh0m5f1Wzu/64yFkq70PPzT6LXYCTbkOdhpxX1Vw1fg0iSpmC3MRkvqpKTZVmt9Lk1vqL6UefRbhWuWphpFRDHmBMqjuQLVdh7OxpqtQ+iCmbZk0URBs9uf7y3I3H2QhQeQ+XlzsbovaBEe7Fsrk3MTk46+FO302nhoyejVvdYdVvw4/HjVrf7+PHJSbfb6XQ6sgKmSMnvqZttxpusNqO7CA6EHa99HqvG0NasPpQb1G7i0nx47iweRqK25h25ZsKn5ygMLlK2oLu9vCEwCpwStqC8oNrRMvjaFbp7VHPjCp2z7haHONM05J5e86kN4QTdjtPTmSbBtvscxQmYCa5X0cjaFMHsdlqClKALHpqThMis4q+TEn7uCTP+IhFWIXIKd6d8GjLN0HuYoNBrJIgx6JQ7ZrtVG4I/3atcQ2eEmSy3F9cF+qm1z23KpwdtzcWyx5ct3+10drv8zIQRWveMyGGeWviSXX5PGPti7XdNdFVFlAAJG8eFc9X8WXX1y4AwTwCH5DROMRBY+MCgQgKd7pnSt3wElmsEjGxBqyD2bqsK5K0NEo98QTpDW1S9d8/SvmyS5AUbm5x0OsxazXEosx1Gj3cZfWugoMQ6/RGVaIn++Zm4xrloSuiB2G+B2B92iX1p3UgrhUa0xJnxxXisY81jJkeXae+1Nf6B3W+C3e5txy1jSYxtYR6G8DfB5sm+z2p1KFge+4IcUuskmwe/B3q/bnrLSGZIiWW9Kbc+IM/6R0+2WclqL+qjXNlueG0vtCrbWItRblrpSoOVMvWGaa2YW9enmkwSolwuhYdwog1GMlo+vKxvRz+/uwxnSy6Xi5WicFqnt3aJ371Xh8vxVc3WYOvaurxjsuYztiG0JUb9cLJdSXV8wJCR5CQruI+POkfhJsV4ZRDqeCki7dMN169C29QtVv3xhTTHCl/CGbXzFLThOAuX8lYVr1eSrWUke2vy33qAvXAhrRVJNuJFi8UIPL51aVny6w8FunnFeS0kBGVRac/PSvbGkPpt1Ww93YOL5XntUPzPkuJeTOqrr+GLb7CWPSkjeY3zdWWUBcLPyunr0wc/AwatZDkoI5kgKHSB4OqP51XKrUtevlq4M+EZvmpFP6grt9oO1kbR+W9vLnkcLOXDLAxC6eCG5UC4qYK0gYIwZcK7hUzBTAqYsG3lk4cHbM6erVkTstoLw2JRWVzaazRl2aBC/JuBKct/AOOVaH0= +api: eJztWG1v2zYQ/isEsQ/JIMdO53SrgX5w2xTI1m5Bmq7YMs84i2eLjUSq5CmJa+i/D0dafm+SdR3QAvkmy8fj3fPcnchnJhX61OmStDWyJ49vMK0IBYjUFgUYJbTxWvELVxmjzUSk1hBog05YIyhDQeAmSMJYhQd/GZlIgomXvQv5wqaX6IavwcAECzQ07J+eDFV8izeYykEibYkOePcTJXvy1Hr61Sp83mwSfXBYMpEe08ppmsrexUw+Q3Do+hVlvFf02sMYvxzUg0SW4KBAQufDAgMFyp7MrKfwmEjNKZdAmUykww+Vdqhkj1yFyQYu5zFHmKAh0XhIhEOP7gqVcLYiBucK8grF3hDMNBFDyPP9RFgnchhhLjzmmJJ1Yu8Sp71guh8Ru2lZKHUrtQonaFp4Qw5aEcaZvIJcKyCOvQkyKbR5epiEf4YRf1kn0qcZFsBraFqyvSenzUQmstDmFZoJY3VY18kCDK3+FQwLWoRWaEiPNTqxx744y5MX+wfideVJeAJH4lpTJsAIyMsMTFWg06lIM3CQEjrB1VXAtCkokSMxV4lQeqLJJ6IyCp1PrUN+afkVr8mmZYbG/xfk7g8WVxGHxdn/fQGtj/3Wn53Wk8HycXjQGnz/nay54ngX9PTMqik73qihDBddRVbMS7VpMAr/zuE92MEE/4mG2C+UZa7T0DXt956dz7bTsaP3mJJMZOm4x0hjwGQewYohOAdTuU31vSPVhIXfBrJOJN5AUebIDZp7mchWDjKRbe77zyWOvZqrO8PvK6X5EXKB5ko7a3gAiStwGkY5eqGN+OX4j6e/91+9PRZj6wqg23KpE3lt3aU2k6HSblfVrO//LhoLpV3o+ekn0VtgJNtQlmGnJfUXC74GiSRNwW45E89ivcm6jst8aY2PPD/qPNouwWX/MqcC0hRLQsVhfKHyem9HQ6124RMxlj1ZVWHqbDfHezsSJy9E5TGUXelsit4LyrQX885aB+zoqIM/dTudFj56Mmp1D1W3BT8ePm51u48fHx11u51OpyMjMFVOfkfRbNK9yGo9urPgQNjxyocxdoW2ZvmJXON1HZfFV+fOymEkGmvekQsmfHcOwtQiZSu628sbAqPAKWErKitqHM2Db1yhu0cpL1yhc9bd4hBvNA25oVd8akM4Qbfl9PhGk2DbXY7SDMwEV6toZG2OYLbbLEPK0AUPi5OEKKziT5MSfuoJC/4cEcYQOYW7Uz4OmRboPUxQ6BUSxBh0zh2z2acLgj/RqFxAJ4SFrDdXNtW5c+Fzm/OhQVtzNu/uebN3O53t/j4xYXI23SJKmOYWvmR/3xPAvlj53VAc64cyIGHTtHIuTp5lP78M2HLvOySn8QoDdZUP3Ckk0PmO4XzL7J+vETCyFS2D2LmtqpC3Nkg86QXpAm0Vu+6eRX2+SJIXrG1y1Okwaw3BocC2GD3cZvStgYoy6/RHVKIl+qcn4hKnYlE/D8R+C8T+sE3sS+tGWik0oiVOjK/GY51qHjAlukJ7r63xD+x+E+x2bztoGUtibCvzMIS/CTaPdn1W43FgfuALEkijjawf+R7o/brprRNZIGWWlabS+oA8yx492Wb1qj1rDnF1e8FrOypL7ZlWdRsbHcpdRVVpsBSl3jC7kcBVaWqRUEZUyrnsEI60wUgm84eXzfXo53fn4XDJVXO21BOOmyxXrvDbt+pwNb5oSBtsXFrnN0xWfMY2hDaHqh+Otkuhjs8ZMpGcZET98KBzEK5SDFsBoZznEtIuyXD1LrTJ4GzZJl9Iboz4Et5Qu8xBG46zcjlvFem9kGwtE9lbEf/WAgwcs0G4mjbaJFvz6tlsBB7furyu+fWHCt00kt/oCUFgVNrzs5K9MeR+UzxbzXvvbH5+2xf/s7K4E5zmEmz4ChysZU/KRF7idFUgZZ3ws3L6+mTCz4BBK1kP6kRmCApdIDj+8Tym3Drn5cuFWxOf4Ysr+kFnudV2sDKaTn97c85zYa4iFmEwSgfXrArCdQzSBgrCuAnvZjIHM6lgwrbRJ08RWB9CG0MnZLUThtksWpzbSzR1vUCF+DcDU9f/AH7OaKI= sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/post-node-container-pull.api.mdx b/docs/docs/gen/api/post-node-container-docker-pull.api.mdx similarity index 92% rename from docs/docs/gen/api/post-node-container-pull.api.mdx rename to docs/docs/gen/api/post-node-container-docker-pull.api.mdx index 43b08b53..e967753b 100644 --- a/docs/docs/gen/api/post-node-container-pull.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker-pull.api.mdx @@ -1,11 +1,11 @@ --- -id: post-node-container-pull +id: post-node-container-docker-pull title: "Pull a container image" description: "Pull a container image on the target node." sidebar_label: "Pull a container image" hide_title: true hide_table_of_contents: true -api: eJztWNtu2zgQ/RWCTwkg31In2xWwD+kN8GIvQZqiD4lhjMWxxEQiVXKUxDX074uhZMW3bLuLLrAF+iZb5FzOOTPkaCUV+sTpkrQ1MpYXVZ4LEIk1BNqgE7qAFIU1gjIUBC5FEsYq7N8YGUmC1Mv4Wr5er5/9DgZSLNDQ7PxiMusMzYIhOY2kLdEBu5sodmg9/WEVdhY4AhlJj0nlNC1lfL2SrxAcuvOKMnbW2YwfnCaU03oayRIcFEjofNhhoEAZy8x6Co+R1JxeCZTJSDr8VGmHSsbkKox2MLhqsoQUDYm1hUg49OjuUQlnK9ImFfeQVyiOZmCWkZhBnh9HwjqRwxxz4THHhKwTR3e4jMPS4wazx56FUvcSqzBF08NHctBrgFzJe8i1AuLY10FGhTa/jKLwZtYwIOtI+iTDAngPLUte78lpk8pIFtr8hiZlsEY1Y8OW0NMrq5a8fifbDFuSyYqyyvP+AYAYcjTEu6Esc50EAge3nk2s9mOx81tMSEaydEw3aQzZNRo4EPJ2SJMQjsMFOjRJF5g4wn7aj8SNNKk2j3EOhJ5uJP+jbHKHrq/tINdzB245gLzUBuMX/dHLG9lCj49QlDl7bt82Jv41KbKu602wrmWnctIUHG3p+rIhgreFfb60xjfQnAxP9rlpgAi5Q5JgSaiYnW/Exq2dz7Q6RMfCugJIxrKqtNqjhxVza+di8kZUHlWgx9kEvReUaS9aufW3AD89HeLL8XDYw5Of573xSI178NPorDcen52dno7Hw+FwKBtQqpz8RlTgHCy5gAkLfyir7egugwFhFwJMK+wAYNd2+ntAdF3ii8rk1Ner2QU3xdAn+hx78PYMpPt2OCpUbYiTN9tw+QxOTs9iGM1Pkhdq3O8HBwTp19luCxpSQRmQeADfutv20sqfu4n+vJm+NoQpuk0paENn42dKlXcLbcR8SehDpEkGJsVNJObW5ghmz8LHDClDF6DsOBKFVXqhUQm/9ISF8ASEwTI6Z92XUXjLy0SB3nOAeoMqsQAdkNit3U4Gz5cvS2tCWOwX/lq3z+19bXM+ELQ1l23Zt11gPBweKHwTes66lEQJy9zCtyz+r4TxXGz8Xis+7G2UZZOkcm5XV+8CwtwYHJLTeI+BwKrRhkICnfuvcK6U5kfIRbtHwNxW9BTEQbeqCkeGQXqw7k6QLtBWTYVyfz8o8/0SapLkDVtOTodDZm3NcZDZHqOjfUY/GKgos05/RiV64vxiIu5wKToJ/SD2eyD2xT6x76yba6XQiJ6YGF8tFjrR3GZKdIX2Xlvjf7D7HbB7eqgRN8cIn5184w/H6o8m/P9ns45kgZRZnjBLG274YfSL5YCH18FqfdbXg26aHJTrydPdN2Pk9GkMfc+MNqRtDqNdEhlRKduhLNx2wiIZtQ/v1reoXz9ehXsHK+XyaSx7u86sm5F2JhS+XZqFDR7brM/DZeZp3uYzRUaSY28AHPWH/XCnZgQKCMps5+LDY/4uBasnnf/jDwMNLISPNChz0IbjqFzORhsmriWvlpGMN4b0zjwTxnRMo3At4+Wr1Rw8fnB5XfPfnyp0y4ake3Aa5gzK9Uoq7flZyXgBud8d7jdTOrpsD99j8R+P/AfRWI83hoebsFrGUkbyDpebXy7qaR3JDEGhC/k1r183WfSu2MjT9r1mVEfrHedhgPzbtdONqrn48/0Vy7f9ZlCEmpUOHvjrADw0odqAaqiK8N9K5mDSqhFwY5PFDtu1slMbIauDYKxWzYore4emrjtsiH8zMHX9FxHRfdc= +api: eJztWNtu2zgQ/RWCTykg31In2xWwD+kN8GIvQZqiD4kRjMWxxEQiVXKUxDX074shZce3bruLLrAF+ibLnNs5Z0iOllKhz5yuSVsjU3nelKUAkVlDoA06oSvIUVgjqEBB4HIkYazC/rWRiSTIvUyv5Gub3aG7+R0M5FihoZuz88mNim+DCzlNpK3RAQeaKA5lPf1hFb5axYpOOAOZSI9Z4zQtZHq1lC8RHLqzhgoOFt2mD04Tymk7TWQNDiokdD4sN1ChTGVhPYXHRGqurQYqZCIdfmy0QyVTcg0mOwBcxhIhR0Ni5SERDj26e1TC2Ya0ycU9lA2Koxswi0TcQFk+S4R1ooQZlsJjiRlZJ47ucJGGpc8iYI89C7XuZVZhjqaHj+SgF1FcynsotQLi3FdJJpU2v4yS8M9NhF+2ifRZgRWwDS1qXu/JaZPLRFba/IYmZ6RGLWPDntDTS6sWvH6n2gI7hsmKuinL/gGAWAxoiK2hrkudBQ4Ht55dLPdzsbNbzEgmsnbMOGkM1UUZHEh5O6VJSMfhHB2abJ2YOMJ+3k/EtTS5No9pCYSeriW/iYLoazso9cyBWwygrLXB9Hl/9OJadtDjI1R1yZG7f6OLf02KbNt2E6wruRY6aQqBnhR9EVlgm2Dka2t8xOV4eLxPTEQhFA5ZhjWhYmq+ERW3dnaj1SEu5tZVQDKVTaPVHjcsl1s7E5PXovGoAjfOZui9oEJ70Wmtv4X2yckQX4yHwx4e/zzrjUdq3IOfRqe98fj09ORkPB4Oh0MZQWlK8htZgXOw4O4lrPyhqrazuwgOhJ0LMJ2qA4Drbae/B8R6i/iiLLn01WoOwdth2CT6nHuI9hlI9/1wVqi6FCevt+HyBRyfnKYwmh1nz9W43w8BCPKv8911M+SCCiDxAL4Ltx2l0z5vJfrTZvnaEOboNqWgDZ2OP9OnbC20EbMFoQ+ZZgWYHDeRmFlbIpg9Dx8KpAJdgHLNkais0nONSviFJ6yEJyAMntE5676MwhteJir0nhPUG1SJOeiAxG7jrmXwmd5lXU0Iq/2WX4n2oOErW/I5oK256Bq+6//xcHig5U3YalZNJGpYlBa+Zdt/JYBnYuP3SuvBNmrKZlnj3K6i3gZseUtwSE7jPQbqmqgKhQS69F8RXCnNj1CKzkbAzDb0lMTBsKoJJ4VBerDuTpCu0DaxN3lbPyjw/eaJRbLBVpCT4ZBZWxEcBLbH6Gif0fcGGiqs059QiZ44O5+IO1yItX5+EPs9EPt8n9i31s20UmhET0yMb+ZznWneYGp0lfZeW+N/sPsdsHtyaCOOBwifmnzRDwfqj034/89mm8gKqbA8W9Y2XOzDxJfKAQ+sg+XqlG8H6/F2EEeHQb0aOt19HCKnTxPoOyY2crc5h65rKYhq2Y1k4boTFsmke3i7ukb9+uEyXDxYMBdPQ9mbVYHrCWlnPuHrpZnbELEr/izcZp6mbT5aZCI594jjqD/sh0s1A1FBEGg3FR+e8HeZWD7J/R9/E4iwED7SoC5BG86jcSU7jYRcSV4tE5lujOhr95xKYIUJZF6mSbigsd1yOQOP713Ztvz6Y4NuEdm6B6dhxuhcLaXSnp+VTOdQ+t0Zf7O2o4vuMH4m/uPJ/yAsq0HH8JgTVstUykTe4WLzA0Y7bRNZICh0ob7496tYRe+SnTyZ721ObbKyOAuj5N+unW500fmf7y5Zx92ngyr0sHTwwB8J4CGmagOqoT3Cu6UsweRNVHL0yaqH7abZaZJQ1UEwlsu44tLeoWnbNTbEvxmYtv0L1wF9/A== sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/post-node-container-start.api.mdx b/docs/docs/gen/api/post-node-container-docker-start.api.mdx similarity index 93% rename from docs/docs/gen/api/post-node-container-start.api.mdx rename to docs/docs/gen/api/post-node-container-docker-start.api.mdx index d392628c..67b149d2 100644 --- a/docs/docs/gen/api/post-node-container-start.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker-start.api.mdx @@ -1,11 +1,11 @@ --- -id: post-node-container-start +id: post-node-container-docker-start title: "Start a container" description: "Start a stopped container on the target node." sidebar_label: "Start a container" hide_title: true hide_table_of_contents: true -api: eJztWFFv20YM/ivEYQ/JIDtO53SrgT147Qp4WLugSVFgmWfQOtq6RLpT76g0rqH/PvBky07srlnXAi2QJ59kkkd+H3kib6k0hdSbko2zaqDOGD0DQmBXlqQhdZbRWPLgLHBGwOjnxGCdpu5fViWKcR7U4EI9XUtOXqDFORVkeTI8HU1aExNXkkfZKKhxotqnkVYDdeoCv3SaWjPRE5WoQGnlDS/U4GKpfiH05IcVZ7Jla3nwzhsmNa7HiSrRY0FMPkQNiwWpgcpc4LhMlJE4S+RMJcrT28p40mrAvqLkDhjnTaw4J8uwtpCAp0D+mjR4V7Gxc7jGvCI4mKBdJDDBPD9MwHnIcUo5BMopZefh4IoWgyh62CB303FYmk7qNM3JduiGPXYaOJfqGnOjkcX3tZNJYezPx0n8Z9LwoOpEhTSjAkWHF6XIB/bGzlWiCmN/JzsXsI7rOmnBMPo/wdBSAkaTZTMz5OFAbEmUo2eHXXhRBYYQc+ed4QzQAuZlhrYqyJsU0gw9pkwe0GoocLFOLMiJhasEtJkbDglUVpMPqfMkL528Ep1sUWZkw/9B7v5gSRaJWxL93xfYeT/s/NnrPBlvlpNuZ/z9d6qWjPMUSmcDxd0f9R7Jz4cQbDDCNKWSSXdVEpOYLIsSlmVu0lgTR5dBNJe7DrvpJaVSGKWXCmLT7HvpphOj9wU2c75AVgNVVZH4OzmeEVy6KYyeQRVIAzsovUspBODMBBAIKbB4SjdYlLnYPjnp0U/9Xq9Dj55MO/1j3e/gj8ePO/3+48cnJ/1+r9frCdqeQpVz2PIKvceFZB9TEfZFddu7V9EAuBng1lGUmxmlizQnwFQEuztotOW+B4/d+NfSso+ccbHguxLAfkQ/Xh5RuaAQcH4PH84YuQqwkr8NdXo7dUiL5TRDO6dt36bO5YR2x/SbjDgjH8NqD1wonBY3NYRFYCrENFP0mbx3/uMe/ypia4fBbMEGMzS5ZHZdb58rFxtKxoliwzG4Frph5LFhe8RUqPqu+jqVPqz91OVy0jZ2YkGKlTpR/V5vtyRHNp4T6/yGzWfjMxblPdEcwtbzOgmjLnCGDC5NK++b42KTGc8j0FKwntgbuqbIYxUikZoYTR7usbnWRpaYw0oHcOoq3jixd1tdkWxtid85fwVsCnJVUzRyLG/tayzTnPzeymuCFIVbm5z0ekLcmuiYbTukHu+S+tpixZnz5j1p6MDwdARXtIA2jx6I/RaI/WGX2OfOT43WZKEDIxuq2cykRk6bknxhQog95QO73wK7/X9rj6xjmLnKfs7O6IHNL8bmyb4va9MbxG5FhqO2gXmg9OunNHatnDmZyEsXIvIyIg7UkUz8R8t1F1cftbweLY2uj0I7rvvrZvYeb2b3M+G1oW57gm9DyZhLtRrOYjcbhVSyWjxfTzC/vTmPfaWxMxfVV4EMY+e5uXeQL79KlDjSYHLc7XXjRCJBFRiTbTUMr2882njuArrcZO2nXI80ETLd8FGZo7HiReVzsdtAe6FEWiVqsHVJse3NII5tDcDjJDbSorVcTjHQa5/Xtbx+W5FfNLBfozc4FWQulkqbIGutBjPMw93hfju4g1erPukQvvDNx15Q1hOilfkwSquBUom6osX2BY7cY3xSTF/fNcYnwGC0qsd1ojJCTT4S3PwxjFcKWyo756tcVbTFffrH2blKFN6uwzt1F83v9We5bCTO3RXZum7dY3kWD+v6H7pgEhs= +api: eJztWG1v20YM/ivEYR+SQbaVzulWA/vgtSvgYe2CJkWBZZ5B62jrEulOvaPSuIb++8BT/JLYXbOuBVognyzLJI98HpImb6k0hcybio2zaqBOGT0DQmBXVaQhc5bRWPLgLHBOwOjnxGCdpu5fViWKcR7U4Fw9c9kl+ckLtDinkixPhiejiW7fuoo8yhFBjRO1/jbSaqBOXOCXTtPT1VGtpeiJSlSgrPaGF2pwvlS/EHryw5pzObI1PnjnDZMaN+NEVeixJCYforjFktRA5S5wfEyUkSAr5FwlytPb2njSasC+puQOEmdtoDgny7CykICnQP6KNHhXs7FzuMKiJjiYoF0kMMGiOEzAeShwSgUEKihj5+HgkhaDKHrYwnbdcViZTuY0zcl26Jo9dlosl+oKC6ORxfeVk0lp7M9HSfxl0pKgmkSFLKcSRYcXlcgH9sbOVaJKY38nOxekjpomWYNh9H+CYc0KGE2WzcyQhwOxJVGOnh124UUdGEJMnHeGc0ALWFQ52rokbzLIcvSYMXlAq6HExSqroCAWrhLQZm44JFBbTT5kzpO8dPJKdPJFlZMN/we5+4MlWSRuSfR/n2Pn/bDzZ9p5Mt48Trqd8fffqUYyzlOonA0UT3+UPpKPDyHYYoRZRhWT7qpECRBkWZSwqgqTxbLoXQTRXO467KYXlElVVF6KiE177oWbTozeF9jM+RJZDVRdR+Lv5HhOcOGmMHoGdSAN7KDyLqMQgHMTQCCkwOIpXWNZFWL7+Diln/pp2qFHT6ad/pHud/DHo8edfv/x4+Pjfj9N01TQ9hTqgsOWV+g9LiT7mMqwL6rb3r2KBsDNALf6UGFmlC2yggAzEezuoLEu9z147Ma/kpZzpMHFgu9KAPsR/Xh5ROWSQsD5PXw4ZeQ6wI38baiz26lDWixnOdo5bfs2da4gtDum3+TEOfkY1rrnQum0uKkhLAJTKaaZos/kvfMf9/hXEVs5DGYLNpihKSSzm2a7r5xvKBknig3H4No2P4wktlSPmErV3NVd5dEHVJ+6QnpsaySWophoEtVP091iHNnYIVaZDZs/jM9YjvfEcQhb31fpF3WBc2RwWVZ73zaKTU48jxBLqXpib+iKIoN1iBRqYjRFuMfhWht5xAJudACnruaNE3uP1TXJ0Zb4nfOXwKYkV7flIg1561xjmebk99ZcG6Qo3DrkOE2FuBXLMc92SD3aJfW1xZpz58170tCB4ckILmkB6yR6IPZbIPaHXWKfOz81WpOFDoxsqGczkxnpMxX50oQQB8oHdr8Fdvv/NhhZxzBztf2cM9EDm1+MzeN9/6ztVBDnFFmL1qPLA6VfP6VxXuXcyTpeuRCRl+VwoHqy6PeWq/mt6a157bXbd29pdNML62XdX7XL93izuZ8KvS2D2/v7OqKcuVI321kcZ6OQSm4enq9WmN/enMXB0tiZi+o38Qzj6Lm5eJABQCVKHGmhOeqm3biSSGwlxpy72YZX9x3rsO7iutwk76dcjrQRMl1zryrQWPGi9oXYbRE+VyKtEjXYuqW45U2EWQTiAtciPU7iSC3qy+UUA732RdPI67c1+UWL/xV6g1OB6HyptAnyrNVghkW4u+ZvR3nw6mZuOoQvfAeyF53VrmhlU4zSaqBUoi5psX2VIzcanxTT13eh8QkwGK2acZOonFCTjwS3Pwzj5cKWyk6/lUuLdbGf/HF6phKFtwvyTgFG83v9WS5biTN3SbZp1u6xfBcPm+YfH+QTiw== sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/post-node-container-stop.api.mdx b/docs/docs/gen/api/post-node-container-docker-stop.api.mdx similarity index 92% rename from docs/docs/gen/api/post-node-container-stop.api.mdx rename to docs/docs/gen/api/post-node-container-docker-stop.api.mdx index f6ac7ca8..8485685b 100644 --- a/docs/docs/gen/api/post-node-container-stop.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker-stop.api.mdx @@ -1,11 +1,11 @@ --- -id: post-node-container-stop +id: post-node-container-docker-stop title: "Stop a container" description: "Stop a running container on the target node. Optionally specify a timeout before the container is killed." sidebar_label: "Stop a container" hide_title: true hide_table_of_contents: true -api: eJztWG1v20YM/iuHwz4kg+zIrdOtAvrBTVsgw7oGSYoCyzyD1tHWJdKdekclcQ3994Enyy+x22ZdO7RAvskyySOfh6R4nEuFPnW6JG2NTOQZ2VKAcJUx2kxFag2BNuiENYIyFARuiiSMVdgVb4IW5PlM+BJTPZkJEKQLtBWJMU6sw6C0sqK9uNJ5jqr7l5GRJJh6mVzIo1Zg9BoMTLFAQ6PByfFoqTmyJTrg47wcRnL561jJRJ5YT39YhUszHIWMpMe0cppmMrmYy+cIDt2gooxPXBpObpwmlMN6GMkSHBRI6HzQMFCgTGRmPYXHSGqGqATKZCQdvq+0QyUTchVGd3A8b3CCKRoSrYVIOPTorlEJZytigK8hr1DsjcDMIjGCPN+PhHUihzHmwmOOKVkn9q5wlgTR/Qa4246FUndSq3CKpoO35KDToDmX15BrBcS+t05GhTbPelH4Z9RwKOtI+jTDAliHZiXLe3LaTGUkC21+RzNlsHp1HS3B0OpfwXC0Yl6hIT3R6MQe2+Ioj1/sd8XrypPwBI7EjaZMgBGQlxmYqkCnU5Fm4CAldAKMEgXM2nQSORJzFQmlp5p8JCqj0PnUOuSXll+xTjYrMzT+vyB3f7A4i9gtjv7vC+h8GHT+jDtPh6vHUbcz/PknWXPG8Sno6blVMza8CV5bXsJzUa6Ss7sB+wRyj1FIaDTEVqAsc52G8ji49Gxqvu28HV9iSjKSpeNiIo0BgUX1rglqQzhFJ+9Se4apNcoLsuIG9LLeubw5sTfqvite4ASqnIJ4L26oKLTRRVXIJI5kAbfN8+M45pOCtEx68f0Zs4UmLEqaBcriqIDbZ4/jWNZ1HUnSlON6QnKLOG3QZ4maIfWlNb7B4VH8aJuQVTYHRiBNsSRUzMdXgv/Sjkda7cqxiXUFkExkVYUavNNuMhSXdiyOX4jKo2KUS2dT9F5Qpr1Y5Bl7irdQlAGLw8MYf+3HcQcfPR13+j3V78AvvSedfv/Jk8PDfj+OGb0ADFO35hU4BzNuBISF3xXVpnenwYCwEwFr34JcTzCdpTkKSFmwu4XGsvPuwGM7/laaz+HsC723ywHsRvTznSooF+g9TO/hwxkBVV4s5DehTtcyBxyhYstpBmaK676Nrc0RzJbpdxlShi6Etfz0icIqdlMJP/OEBZsmDD6jc9Z93uOXLNY6LPQabGICmr/SsimMttdcrCgZ7qipQeCxYfuYsJD1XfU2lT6ufWRz/ug1dkI9LsqzH8fbFXlsQgNo8/tOk/xKRXlPNAdi7XebhEFXUAYkbJpWzjXtYpUZrwLQXLAOyWm8xsBj5QORCgl07u9xuFJ68bVY6AgY8xi2dGLnsapCPtog3Vh31c5u4Wjut/f4DJwvg2SFjUMO43i99YZs2yK1t03qWwMVZdbpD6hERwxOjsUVzsQyjx6I/RGIfbxN7CvrxlopNKIjjo2vJhOdau42JbpCex+m+wd2fwR2+5+ajowlMbGV+ZqT0QOb34zNw11f1mY24Dm33FgEPFD6/VMaplbKLO9GSusD8nxbT+QBL24O5u0UVx8seT2Ya1Uf+HZx4q6bLchwtUU5Y1ob5tZ3KctIMqKgG8R4mA1CMlo8vGovML+9Ow9jJafL6er++7INb+0S2ot5ejcT27xt4h2EAXW1KOIBQUaSHW6g63Xjbri4cOwFhJxcrC8W661l1Hdhn69y+/9ehTUgEt7SQZmDNhxA5XL2qSHvQvJRMpLJ2kZqPZIkXAwDhcMoTOqsNJ+PweNbl9c1v35foZs1xF6D0zBmTC/mUmnPz6uFwkdx2TtdDGL74htvuXZi0l5BDV9Ag7RMpIzkFc7Wl3W8s/qimL6/ldUXwKCVrId1JDMEhS4Q3Pxx1ITcOWf1leJWG2f4Go1B2HJ8Una41m9O3pydc80vNlpF6HbSwQ0vreCmcdKWzSY3mTfv5jIHM63CNVs2NrlDwGaDudNQQlQ7YZjPG4lze4WmrpeoEP9mYOr6Hz6Q/hg= +api: eJztWG1v20YM/iuHwz4kg2zLrdOtBvohTVogw7oGSYoCy7yA1tHWNdKdekclcQ3994F3ll/itM26dmiBfJNlkkc+D0nxOJcKfeZ0RdoaOZSnZCsBwtXGaDMVmTUE2qAT1gjKURC4KZIwVmFXvA5aUBQz4SvM9GQmQJAu0dYkxjixDoPSyor24lIXBaruX0YmkmDq5fBcHtrsEt3FKzAwxRINXewfH12o+NZW6IAP8nKUyOWvIyWH8th6+sMqPGhPiJY4CplIj1ntNM3k8HwunyM4dPs15XxitD28dppQjppRIitwUCKh80HcQIlyKHPrKTwmUjM+FVAuE+nwfa0dKjkkV2NyC8SzCBJM0ZBoLSTCoUd3hUo4WxOjewVFjWLnAswsERdQFLuJsE4UMMZCeCwwI+vEziXOhkF0N6J207FQ6U5mFU7RdPCGHHQilHN5BYVWQOx762RSavOsn4R/LiKBskmkz3IsgXVoVrG8J6fNVCay1OZ3NFNGqt80yRIMrf4VDAcr2hUa0hONTuywLY7y6HC3K17VnoQncCSuNeUCjICiysHUJTqdiSwHBxmhE2CUKGHW5pIokJirRCg91eQTURuFzmfWIb+0/Ip18lmVo/H/Bbn7g8VZxG5x9H+fQ+fDfufPtPN0tHq86HZGP/8kG844PgU9PbdqxoY3wWtrS3iuyFVydjdgn0DhMZEMChpiK1BVhc5ChfTeeTY133bejt9hRjKRleN6Io0BgUXprglqQzhFJ29Te4qZNcoLsuIa9LLYubY5sTeKvisOcQJ1QUG8n0YqSm10WZdymCayhJv4/DhN+aQgLYf99P6M2VITlhXNAmVpUsLNs8dpKpumSSRpKlho1RxOIvT8d8N4+soaH0F4lD7aZmOVyoEOyDKsCBWT8ZWwf2fHF1rdlWAT60ogOZR1HQrwVq/JUbyzY3F0KGqPiiGunM3Qe0G59mKRZOwp3kBZBSD29lL8dZCmHXz0dNwZ9NWgA7/0n3QGgydP9vYGgzRl6AIwzNuaV+AczLgLEJb+rqg2vTsJBoSdCFj7ChR6gtksK1BAxoLdLTSWbfcOPLbjb6X5HE690Hi7HMDdiH6+TQXlEr2H6T18OCWg2ouF/CbU2VrmgCNUbDnLwUxx3bextQWC2TL9NkfK0YWwlp8+UVrFbirhZ56wZNOEwWd0zrrPe/yCxVqHhV6DTUxA8/dZxsJoG835ipLR7YLaDyRGqo8IS9nc1m3z6COqB7bgb100EipxUZiDNN2uxSMT6r7N7Fu98SuV4z1x3Bdrv9v0C7qCciBhs6x2LjaKVU68DBBzqTokp/EKA4O1DxQqJNCFv8fhSunFR2KhI2DMo9fSiTuPVTXy0Qbp2rrLdl4LR3ObvUf3P1sGyQobh+yl6XrHDXm2RWp/m9Q3BmrKrdMfUImO2D8+Epc4E8skeiD2RyD28TaxL60ba6XQiI44Mr6eTHSmuc9U6ErtfZjrH9j9EdgdfGouMpbExNbma85ED2x+Mzb37vqyxqmAJ9xq4/L/QOn3T2mYVym3vBWprA/I8yV9KHu8rOnN2/mt6S157cUtSG+uVdPz7c7EXcUdyGi1QDlldiOB62uUZUA5UdANYjzNBiGZLB5etjeY396ehbmSs+Zkdft90Ua5dgXtpzy+m4mNb2PY+2FCXa2JeE6QiWSHI4L9btoNNxeGoISQmovlxWKztQz+NvrzVYr/31uwCCLhDfWqArThAGpXsE+Rw3PJR8lEDtf2URuRBCJZIFwRA5ejJMzsrD2fj8HjG1c0Db9+X6ObRYavwGkYM7jnc6m05+fVXuGjAO2cLAazXfGNl113gtNeRg1fRYO0HEqZyEucre/seHX1RTF9f5urL4BBK9mMmkTmCApdIDj+cRBD7pyx+kpxq60zfFFjP+w7Pik7Wus/x69Pz7j4F4utMnQ/6eCad1dwHZ20VdzpDufx3VwWYKZ1uHDLaJNbBWx2mludJUR1JwzzeZQ4s5dommaJCvFvBqZp/gEfcf49 sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/post-node-container.api.mdx b/docs/docs/gen/api/post-node-container-docker.api.mdx similarity index 92% rename from docs/docs/gen/api/post-node-container.api.mdx rename to docs/docs/gen/api/post-node-container-docker.api.mdx index 5d948d63..5dd3eeaf 100644 --- a/docs/docs/gen/api/post-node-container.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker.api.mdx @@ -1,11 +1,11 @@ --- -id: post-node-container +id: post-node-container-docker title: "Create a container" description: "Create a new container on the target node. Returns a job ID for tracking the asynchronous operation." sidebar_label: "Create a container" hide_title: true hide_table_of_contents: true -api: eJztWG1v28gR/iuL/ZQAlEzZcppjkQ9O4hRur43hOHdoHUMYkSNxY3KXtzuUrQr874fZFSnqxbGvuBZXIN/4MrP77Lw8szMrmaFLrapIGS0T+c4iEAoQGu9FajSB0miF0YJyFAR2jiS0yXAorpBqq50A8dVMxcV7MTNWkIX0Tum5Fwe31GlujTa1E6ZCC7zL8IuWkSSYO5ncyHftHpO/g4Y5lqhpcnZ5Mek2n3SaTt5Gsnu7yGQiL42jf5gMu2VkJB2mtVW0lMnNSr5FsGjPasp5t27R5N4qQnnb3EayAgslElrnNTSUKBOZG0f+MZKKLVMB5TKSFn+plcVMJmRrjHbMdx0MBHPUJNoVImHRoV1gJqypia2zgKJG8WICehmJCRTFy0gYKwqYYiEcFpiSseLFHS4TL/oyGO1hYKBSg9RkOEc9wAeyMAiWXMkFFCoDYuwtyKhU+s0o8n8mwXmyiaRLcyyBdWhZsbwjq/RcRrJU+kfUczbWqGHb8Ero6K3Jliy/EyxdfKQcNsposbHl8ICx2PyoiVeCqipU6pWOvjpebrWPy0y/YkoykpVlt5NCf1JVwhwPwX8MnlcQFmdoUacoXuBwPozEF6nnSj8kBRA6+iJfMmZ8gLIqeNn+z//Y+LJponVIPYX3o3+AQrB4yKYcN0m4Da5cDjw+9mdqyhJ01tsBrIXlAYN4OUFG2FoLpfc3UISl24fa9La+CYaRkRz4MwCWRgszm/1Z3rKcXjwJ5FwvlDWac10swCqYFugYz9/O//nmp7MfP5/z8UugZ0O6/Hh1/eZ1/DqWkXx//vbzX95wxHlAlbHknoR0aSyJEqpK6bmHwtk7Yd1kw0T82iLzCfksbAwr8chej8cnyXh84nEtTFGX+DSyn7ycKE2tqYcMKO8jA8p/O7KjDAiSowXYo0JN/ZuHBjWZiSOw1FthakyBoPfw/Zwj5Wg5qrzKdlAJVZaYKSAslgJm1COLoXiPM6gLcqzL/grQs/A1kAanz4ZGbta5fxtJUuQTocvyULquAmHJpgmarjLaBSsfx8fP4jBIU6wIMw6+34mxvprpRGWHKCD4TCayrlW2Z9vrHNvqWjv0mVtZk6JzgnLlxJqet6nh9DTG1+M4HuDxD9PBeJSNB/Cn0avBePzq1enpeBzHcSyDcdj4BwJwN3i6U22j+1SXJdilUDqcwhtvamoSsM0q28boKuuTjMjHb6WFmYVLBdfWIeM/bNBHa0CGmtRM7RIpjKbH6Uk2xtPZK/lsrt6sy/KPU/MzS9WFL1DexdPlt2h/qyZxKSdfdJ6EW1vLZLtJS6+4vbattV5zhM8F/E327dKHVImOoKy2Vz+Oj8eDeDQYnV6P4uQkTuL4X36rHPR8a6sniSbHzW1SlCZjr2bCLR1h2R6Mic5aY58+wjmLiRKdYx+oXpSJGaiCeWCXhLoIPsRDV2vOkc2uWptwj2jVBb0zBd/9lNH9VZpIjuN4n7sutL92tCwgKlgWBn5P3nqmCc9E771NVK8rKAcSJk05ALPtiPjgretvI0hW4QK982rnvZchgSoOlLC9zbNMrS9Oa501CXUgDm6b1chba6R7Y+981Jo6EAtf8Xr7Kk04953FPjuFQ7LC1ianccxea73sQ2zPo6N9j37WUFNurPo3ZmIgzi4vxB0uRRdE3x37/+DYk33HfjB2qrIMtRiIC+3q2UyliimmQlsq53x3+927f3zvnh4i4lBCQgXU8+3a/d2lf2yXNpEskXLD06TK+E7fj3oSecRzrqNVW+ybo3RryGQXYWJ0u5k4fWJnBn/1504d/pyokuv5i7/peCEZrR8+tL3AX3++9ncODpKrzQTmvD1UNwLZnVGsZ1fdDbQ3G/hW277u2h9vo7su+nA322tmH2kqt3tK39jxXG1mvHXWzjnzl67NHJDrn4wk2zn4eTSMh751YUeV4BNofeRucNl30laUrDap+D8ccwbHEz7QUVWA0oy+tgXDCWF2I3kfGcmkN3HcHOI28rdNllutpuDwsy2ahj//UqNdhvhrZyh+fpkpx8+ZTGZQuN0RZd8KL67W94qX4r88uDxohrbp1NxyemmZSBnJO1z2568NTyRyhAytP1/4/S6cYnDNi2zU9yi2iVqNM9/Wf1P2tscFlx8/XXNmriefpWciaeGe55pwH6CaKsylk1X4tpIF6HkdcjOs2YR5Sp8GdtLen+qgMVarIHFt7lA3TWcb4nc2TNP8Cg1HNdY= +api: eJztWG1v2zgS/isEP7WAbMuJ0+3q0A9pmy6yL9cgTXdxlwbGWBpbbCRSJUdOfIb++2JIW5Zf0mQPe4ddoN/0wiGfeXuGM0uZoUutqkgZLRP5xiIQChAa70RqNIHSaIXRgnIUBHaGJLTJsC8ukWqrnQDx2UzE+VsxNVaQhfRW6ZlfDm6h09wabWonTIUW+JT+Jy0jSTBzMrmWb016i3b8C2iYYYmaxqcX5+MsfG1lnLyJZPt2nslEXhhH/zQZvlmDDDvJSDpMa6toIZPrpXyNYNGe1pTzaWHf5M4qQnnT3ESyAgslElrnl2soUSYyN478YyQVm6UCymUkLX6plcVMJmRrjHZsdxWsAzPUJNY7RMKiQzvHTFhTE5tmDkWN4tkY9CISYyiK55EwVhQwwUI4LDAlY8WzW1wkfunzYLH7noFK9VKT4Qx1D+/JQi+YcSnnUKgMiLGvQUal0q+Gkf8zDp6TTSRdmmMJLEOLitc7skrPZCRLpX9GPWNLDRu2De+Ejl6bbMHrdyKlDY6UY0YZLTa27B8wFkcTauKdoKoKlXqhwWfH2y33cZnJZ0xJRrKy7HlS6DVVJczwEPyH4HkBYXGKFnWK4hn2Z/1IfJJ6pvR9UgCho0/yOWPGeyirgrft/vyvjS+bJlqF1GN43/sHKAQvD6mU4yYDt8GVi57Hx/5MTVmCzjongLWwOGAQv06QEbbWQun9AxRh6fahNp2jr4NhZCR7XgfA0mhhptN/yBtep+ePAjnTc2WN5lwXc7AKJgU6xvPT2b9e/Xr688czVr8EejKki/eXV69exi9jGcm3Z68//vCKI84Dqowl9yikC2NJlFBVSs88FM7eMcsmrYH86xqZT8gnYWNYiUf2cjQ6TkajY49rboq6xMeR/erXidLUmjrIgPIuMqD8jyMbZECQDOZgB4Wa+DcPDWoyY0dgqbPDxJgCQe/h+y1HytFyVHmR7aASqiwxU0BYLARMqUMWffEWp1AX5FiW/RWgZ+FrIA1Onw2NXK9y/yaSpMgnQuD8ULQuA1vJpglirjLaBRMfxUdPIjBIU6wIM468P4muPpvJWGWH8j84TCayrlW2Z9irHNd1tXbo07ayJkXnBOXKiRU3b/PCyUmML0dx3MOj7ye90TAb9eC74YveaPTixcnJaBTHcSyDcdjyB6JvN3JarbbRfajLEuxCKB208MabmJoEbFPKtjHasvooHbL669XCTMN1ggtrn/EfNuiDBSBDTWqqdlkUhpOj9Dgb4cn0hXwyUW/25fUP8/IT69S5r07exZPF1zh/qyBxHSdfcR6FW1vLTLvJSS+4vbettV4RhM8F/EP2bdOHVImOoKy2dz+Kj0a9eNgbnlwN4+Q4TuL43/6oHPRs66hHWSbHzT1SlCZjr2bCLRxhuVaMWc5aYx9X4YyXiRKdYx+oTpSJKaiCeWCXgdoI3iOhyxXhyGZXZp1th0Tqgt6Ygq98yujuFk0kR3G8z1rn2t821vkvKlgUBv5Mxnqi8U5F532dol5WUA4kTJpy6GXbsfDO29VfQpCswjl6t9XO+y1DAlUcqFx7h2eZWt2XVjIr+mlBHDw2q5GP1kh3xt76eDV1oBS+2XXOVZpw5ruJfV4KSrLA1iEnccxeW7vYB9eeR4f7Hv2ooabcWPUfzERPnF6ci1tciDaCvjn27+DY433HvjN2orIMteiJc+3q6VSlismlQlsq53xf+827f33vnhwi4lA8Qu3Ts+2q/c2lf22XNpEskXLDc6TK+AbfT3gSOeDZ1mC5LvPNoPXrINvMl+w8zItuNsOmD+zT4LbuyKlVIyeq5Gr64q86fpGMVg/v1s3Aj79d+UsHx8rlZv5yttatHYDsTihWk6v2CtqZDHytaV/17A830W0PfbiX7bSyD7SU2x2lb+t4qjY13jorH536W9dmCshlUEaS7RzcPezHfd+7sL9K8Hm0UrmdWba+2g2W5SYj/48TzuB4wnsaVAUozehrWzCcEG3Xks+RkUw688YtJULI3UT+3skCy+UEHH60RdPw5y812kUIxPUoxY8xM+X4OZPJFAq3O6nsmuPZ5eqe8Vz8j+eXB+2xbj81N59+tUykjOQtLrpj2IYHEzlChtbrF36/CVr0rniTjfge5TbRWuLUN/hfXXvT4YaL9x+uOEVXA9DSM5O0cMfjTbgLUE0VJtTJMnxbygL0rA5JGvZswlilywc7+e+1OmiM5TKsuDK3qJumtQ3xOxumaX4Hl/U1+w== sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; diff --git a/docs/docs/gen/api/sidebar.ts b/docs/docs/gen/api/sidebar.ts index 241fd909..df7cc564 100644 --- a/docs/docs/gen/api/sidebar.ts +++ b/docs/docs/gen/api/sidebar.ts @@ -86,45 +86,45 @@ const sidebar: SidebarsConfig = { }, { type: "category", - label: "Node/Container", + label: "Node/Docker", link: { type: "doc", - id: "gen/api/container-management-api-container-operations", + id: "gen/api/docker-management-api-docker-operations", }, items: [ { type: "doc", - id: "gen/api/post-node-container", + id: "gen/api/post-node-container-docker", label: "Create a container", className: "api-method post", }, { type: "doc", - id: "gen/api/get-node-container", + id: "gen/api/get-node-container-docker", label: "List containers", className: "api-method get", }, { type: "doc", - id: "gen/api/get-node-container-by-id", + id: "gen/api/get-node-container-docker-by-id", label: "Inspect a container", className: "api-method get", }, { type: "doc", - id: "gen/api/delete-node-container-by-id", + id: "gen/api/delete-node-container-docker-by-id", label: "Remove a container", className: "api-method delete", }, { type: "doc", - id: "gen/api/post-node-container-start", + id: "gen/api/post-node-container-docker-start", label: "Start a container", className: "api-method post", }, { type: "doc", - id: "gen/api/post-node-container-stop", + id: "gen/api/post-node-container-docker-stop", label: "Stop a container", className: "api-method post", }, @@ -132,15 +132,15 @@ const sidebar: SidebarsConfig = { }, { type: "category", - label: "Node/Container/Exec", + label: "Node/Docker/Exec", link: { type: "doc", - id: "gen/api/container-management-api-container-exec", + id: "gen/api/docker-management-api-docker-exec", }, items: [ { type: "doc", - id: "gen/api/post-node-container-exec", + id: "gen/api/post-node-container-docker-exec", label: "Execute a command in a container", className: "api-method post", }, @@ -148,15 +148,15 @@ const sidebar: SidebarsConfig = { }, { type: "category", - label: "Node/Container/Image", + label: "Node/Docker/Image", link: { type: "doc", - id: "gen/api/container-management-api-container-image", + id: "gen/api/docker-management-api-docker-image", }, items: [ { type: "doc", - id: "gen/api/post-node-container-pull", + id: "gen/api/post-node-container-docker-pull", label: "Pull a container image", className: "api-method post", }, diff --git a/docs/docs/sidebar/architecture/system-architecture.md b/docs/docs/sidebar/architecture/system-architecture.md index 1707422e..284f1b82 100644 --- a/docs/docs/sidebar/architecture/system-architecture.md +++ b/docs/docs/sidebar/architecture/system-architecture.md @@ -65,14 +65,14 @@ The API server is built on [Echo][] with handlers generated from an OpenAPI spec via [oapi-codegen][] (`*.gen.go` files). Domain handlers are organized into subpackages: -| Package | Responsibility | -| ------------------------- | -------------------------------------------------------------------------------- | -| `internal/api/node/` | Node endpoints (hostname, status, disk, memory, load, network/dns, command/exec) | -| `internal/api/container/` | Container endpoints (create, list, inspect, start, stop, remove, exec, pull) | -| `internal/api/job/` | Job queue endpoints (get, list, delete, retry, status) | -| `internal/api/health/` | Health check endpoints (liveness, readiness, status) | -| `internal/api/common/` | Shared middleware, error handling, collection responses | -| (metrics) | Prometheus endpoint (`/metrics`) via OpenTelemetry | +| Package | Responsibility | +| ---------------------- | ----------------------------------------------------------------------------------- | +| `internal/api/node/` | Node endpoints (hostname, status, disk, memory, load, network/dns, command/exec) | +| `internal/api/docker/` | Docker container endpoints (create, list, inspect, start, stop, remove, exec, pull) | +| `internal/api/job/` | Job queue endpoints (get, list, delete, retry, status) | +| `internal/api/health/` | Health check endpoints (liveness, readiness, status) | +| `internal/api/common/` | Shared middleware, error handling, collection responses | +| (metrics) | Prometheus endpoint (`/metrics`) via OpenTelemetry | All state-changing operations are dispatched as jobs through the job client layer rather than executed inline. Responses follow a uniform collection @@ -99,17 +99,17 @@ For the full deep dive see [Job System Architecture](job-architecture.md). Providers implement the actual system operations behind a common interface. Each provider is selected at runtime through a platform-aware factory pattern. -| Domain | Providers | -| ------------------- | ------------------------------------------------- | -| `node/host` | Hostname, uptime, OS info | -| `node/disk` | Disk usage statistics | -| `node/mem` | Memory usage statistics | -| `node/load` | Load average statistics | -| `network/dns` | DNS configuration (get/update) | -| `network/ping` | Ping execution and statistics | -| `command/exec` | Direct command execution | -| `command/shell` | Shell command execution | -| `container/runtime` | Container lifecycle (create, start, stop, remove) | +| Domain | Providers | +| ---------------- | -------------------------------------------------------- | +| `node/host` | Hostname, uptime, OS info | +| `node/disk` | Disk usage statistics | +| `node/mem` | Memory usage statistics | +| `node/load` | Load average statistics | +| `network/dns` | DNS configuration (get/update) | +| `network/ping` | Ping execution and statistics | +| `command/exec` | Direct command execution | +| `command/shell` | Shell command execution | +| `docker/runtime` | Docker container lifecycle (create, start, stop, remove) | Providers are stateless and platform-specific (e.g., a Ubuntu DNS provider vs. a generic Linux DNS provider). Adding a new operation means implementing the diff --git a/docs/docs/sidebar/features/container-management.md b/docs/docs/sidebar/features/container-management.md index c63dc478..685eec02 100644 --- a/docs/docs/sidebar/features/container-management.md +++ b/docs/docs/sidebar/features/container-management.md @@ -61,8 +61,8 @@ Container operations follow the same request flow as all OSAPI operations: You can target a specific host, broadcast to all hosts with `_all`, or route by label. See [CLI Reference](../usage/cli/client/container/container.mdx) for usage and examples, or the -[API Reference](/gen/api/container-management-api-container-operations) for the -REST endpoints. +[API Reference](/gen/api/docker-management-api-docker-operations) for the REST +endpoints. ### Runtime Drivers @@ -92,23 +92,23 @@ authentication settings. ## Permissions -| Endpoint | Permission | -| -------------------------------------------- | ------------------- | -| `POST /node/{hostname}/container` (create) | `container:write` | -| `GET /node/{hostname}/container` (list) | `container:read` | -| `GET /node/{hostname}/container/{id}` | `container:read` | -| `POST /node/{hostname}/container/{id}/start` | `container:write` | -| `POST /node/{hostname}/container/{id}/stop` | `container:write` | -| `DELETE /node/{hostname}/container/{id}` | `container:write` | -| `POST /node/{hostname}/container/{id}/exec` | `container:execute` | -| `POST /node/{hostname}/container/pull` | `container:write` | - -The `admin` role includes `container:read`, `container:write`, and -`container:execute`. The `write` role includes `container:read` and -`container:write`. The `read` role includes only `container:read`. +| Endpoint | Permission | +| --------------------------------------------------- | ---------------- | +| `POST /node/{hostname}/container/docker` (create) | `docker:write` | +| `GET /node/{hostname}/container/docker` (list) | `docker:read` | +| `GET /node/{hostname}/container/docker/{id}` | `docker:read` | +| `POST /node/{hostname}/container/docker/{id}/start` | `docker:write` | +| `POST /node/{hostname}/container/docker/{id}/stop` | `docker:write` | +| `DELETE /node/{hostname}/container/docker/{id}` | `docker:write` | +| `POST /node/{hostname}/container/docker/{id}/exec` | `docker:execute` | +| `POST /node/{hostname}/container/docker/pull` | `docker:write` | + +The `admin` role includes `docker:read`, `docker:write`, and `docker:execute`. +The `write` role includes `docker:read` and `docker:write`. The `read` role +includes only `docker:read`. Container exec is a privileged operation similar to command execution. Only the -`admin` role includes `container:execute` by default. Grant it to other roles or +`admin` role includes `docker:execute` by default. Grant it to other roles or tokens explicitly when needed: ```yaml @@ -116,11 +116,11 @@ api: server: security: roles: - container-ops: + docker-ops: permissions: - - container:read - - container:write - - container:execute + - docker:read + - docker:write + - docker:execute - health:read ``` @@ -128,51 +128,69 @@ Or grant it directly on a token: ```bash osapi token generate -r write -u user@example.com \ - -p container:execute + -p docker:execute ``` -## Orchestrator DSL +## Orchestrator -The [orchestrator](../sdk/orchestrator/orchestrator.md) SDK supports container -operations through the -[Container Targeting](../sdk/orchestrator/features/container-targeting.md) -feature. Use `Plan.Docker()` and `Plan.In()` to create containers and run -existing providers inside them without rewriting any code: +The [orchestrator](../sdk/orchestrator/orchestrator.md) SDK can compose +container operations as a DAG using `TaskFunc`. Pull, create, exec, inspect, and +cleanup steps chain together with dependencies and guards: ```go -plan := orchestrator.NewPlan(client, orchestrator.WithDockerExecFn(execFn)) -web := plan.Docker("web-server", "nginx:alpine") - -create := plan.TaskFunc("create", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - return c.Container.Create(ctx, "_any", gen.ContainerCreateRequest{ - Image: "nginx:alpine", - Name: ptr("web-server"), - }) -}) - -plan.In(web).TaskFunc("check-config", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - // Runs inside the container via docker exec + provider run - return c.Container.Exec(ctx, "_any", "web-server", gen.ContainerExecRequest{ - Command: []string{"nginx", "-t"}, - }) -}).DependsOn(create) +plan := orchestrator.NewPlan(client, orchestrator.OnError(orchestrator.Continue)) + +pull := plan.TaskFunc("pull-image", + func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { + _, err := c.Docker.Pull(ctx, "_any", gen.DockerPullRequest{ + Image: "nginx:alpine", + }) + if err != nil { + return nil, err + } + return &orchestrator.Result{Changed: true}, nil + }, +) + +create := plan.TaskFunc("create-container", + func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { + autoStart := true + _, err := c.Docker.Create(ctx, "_any", gen.DockerCreateRequest{ + Image: "nginx:alpine", + Name: ptr("web-server"), + AutoStart: &autoStart, + }) + if err != nil { + return nil, err + } + return &orchestrator.Result{Changed: true}, nil + }, +) +create.DependsOn(pull) + +exec := plan.TaskFunc("check-config", + func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { + _, err := c.Docker.Exec(ctx, "_any", "web-server", + gen.DockerExecRequest{Command: []string{"nginx", "-t"}}) + if err != nil { + return nil, err + } + return &orchestrator.Result{Changed: true}, nil + }, +) +exec.DependsOn(create) ``` -The transport changes from HTTP to `docker exec` + `provider run`, but the SDK -interface is identical. See the -[container targeting feature page](../sdk/orchestrator/features/container-targeting.md) -for details and the -[operations reference](../sdk/orchestrator/orchestrator.md#operations) for all -container operations. +See +[`examples/sdk/orchestrator/features/container-targeting.go`](https://github.com/retr0h/osapi/blob/main/examples/sdk/orchestrator/features/container-targeting.go) +for a complete working example. ## Related - [CLI Reference](../usage/cli/client/container/container.mdx) -- container management commands -- [API Reference](/gen/api/container-management-api-container-operations) -- - REST API documentation -- [Orchestrator Container Targeting](../sdk/orchestrator/features/container-targeting.md) - -- DSL for running providers inside containers +- [API Reference](/gen/api/docker-management-api-docker-operations) -- REST API + documentation - [Job System](job-system.md) -- how async job processing works - [Authentication & RBAC](authentication.md) -- permissions and roles - [Architecture](../architecture/architecture.md) -- system design overview diff --git a/docs/docs/sidebar/sdk/orchestrator/features/container-targeting.md b/docs/docs/sidebar/sdk/orchestrator/features/container-targeting.md index b0e8d246..2657d721 100644 --- a/docs/docs/sidebar/sdk/orchestrator/features/container-targeting.md +++ b/docs/docs/sidebar/sdk/orchestrator/features/container-targeting.md @@ -4,140 +4,120 @@ sidebar_position: 14 # Container Targeting -Create containers and run provider operations inside them using the same typed -SDK methods. The transport changes from HTTP to `docker exec` + `provider run`, -but the interface is identical. +Orchestrate container lifecycle operations -- pull, create, exec, inspect, stop, +and remove -- as a DAG of `TaskFunc` steps using the standard SDK client. ## Setup -Provide a `WithDockerExecFn` option when creating the plan. The exec function -calls the Docker SDK's `ContainerExecCreate` / `ContainerExecAttach` APIs to run -commands inside containers. +Container operations use the same `client.Docker` service as any other SDK call. +No special plan options are needed: ```go -plan := orchestrator.NewPlan(client, orchestrator.WithDockerExecFn(execFn)) +plan := orchestrator.NewPlan(client, orchestrator.OnError(orchestrator.Continue)) ``` -## Docker Target +## Building the DAG -`Plan.Docker()` creates a `DockerTarget` bound to a container name and image. It -implements the `RuntimeTarget` interface: +Chain container operations with `DependsOn` to enforce ordering. Independent +operations at the same level run in parallel: ```go -web := plan.Docker("web-server", "nginx:alpine") -``` - -`RuntimeTarget` is pluggable — Docker is the first implementation. Future -runtimes (LXD, Podman) implement the same interface. - -## Scoped Plans - -`Plan.In()` returns a `ScopedPlan` that routes provider operations through the -target's `ExecProvider` method: - -```go -plan.In(web).TaskFunc("run-inside", - func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - // This executes inside the container via: - // docker exec web-server /osapi provider run --data '' - return &orchestrator.Result{Changed: true}, nil - }, -) -``` - -The `ScopedPlan` supports `TaskFunc` and `TaskFuncWithResults`, with the same -dependency, guard, and error strategy features as the parent plan. - -## Full Example - -A typical workflow creates a container, runs operations inside it, then cleans -up: - -```go -plan := orchestrator.NewPlan(client, orchestrator.WithDockerExecFn(execFn)) -web := plan.Docker("my-app", "ubuntu:24.04") - pull := plan.TaskFunc("pull-image", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - resp, err := c.Container.Pull(ctx, "_any", gen.ContainerPullRequest{ + _, err := c.Docker.Pull(ctx, "_any", gen.DockerPullRequest{ Image: "ubuntu:24.04", }) if err != nil { return nil, err } - return &orchestrator.Result{Changed: true, Data: map[string]any{ - "image_id": resp.Data.Results[0].ImageID, - }}, nil + return &orchestrator.Result{Changed: true}, nil }, ) create := plan.TaskFunc("create-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { autoStart := true - resp, err := c.Container.Create(ctx, "_any", gen.ContainerCreateRequest{ + resp, err := c.Docker.Create(ctx, "_any", gen.DockerCreateRequest{ Image: "ubuntu:24.04", Name: ptr("my-app"), AutoStart: &autoStart, + Command: &[]string{"sleep", "600"}, }) if err != nil { return nil, err } - return &orchestrator.Result{Changed: true, Data: map[string]any{ - "container_id": resp.Data.Results[0].ID, - }}, nil + r := resp.Data.Results[0] + return &orchestrator.Result{ + Changed: true, + Data: map[string]any{"id": r.ID}, + }, nil }, ) create.DependsOn(pull) +``` + +## Exec + +Execute commands inside running containers. Multiple exec tasks that depend on +the same create step run in parallel: -// Run a command inside the container -checkOS := plan.In(web).TaskFunc("check-os", +```go +execHostname := plan.TaskFunc("exec-hostname", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - resp, err := c.Container.Exec(ctx, "_any", "my-app", gen.ContainerExecRequest{ - Command: []string{"cat", "/etc/os-release"}, - }) + resp, err := c.Docker.Exec(ctx, "_any", "my-app", + gen.DockerExecRequest{Command: []string{"hostname"}}) if err != nil { return nil, err } + r := resp.Data.Results[0] return &orchestrator.Result{ - Changed: false, - Data: map[string]any{"stdout": resp.Data.Results[0].Stdout}, + Changed: true, + Data: map[string]any{"stdout": r.Stdout}, }, nil }, ) -checkOS.DependsOn(create) +execHostname.DependsOn(create) -cleanup := plan.TaskFunc("remove-container", +execUname := plan.TaskFunc("exec-uname", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - force := true - _, err := c.Container.Remove(ctx, "_any", "my-app", - &gen.DeleteNodeContainerByIDParams{Force: &force}) + resp, err := c.Docker.Exec(ctx, "_any", "my-app", + gen.DockerExecRequest{Command: []string{"uname", "-a"}}) if err != nil { return nil, err } - return &orchestrator.Result{Changed: true}, nil + r := resp.Data.Results[0] + return &orchestrator.Result{ + Changed: true, + Data: map[string]any{"stdout": r.Stdout}, + }, nil }, ) -cleanup.DependsOn(checkOS) - -report, err := plan.Run(context.Background()) +execUname.DependsOn(create) ``` -## RuntimeTarget Interface +## Cleanup + +Use a cleanup task that depends on all operational tasks to ensure the container +is removed even when some tasks fail (with `OnError(Continue)`): ```go -type RuntimeTarget interface { - Name() string - Runtime() string // "docker", "lxd", "podman" - ExecProvider(ctx context.Context, provider, operation string, data []byte) ([]byte, error) -} +cleanup := plan.TaskFunc("cleanup", + func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { + force := true + _, err := c.Docker.Remove(ctx, "_any", "my-app", + &gen.DeleteNodeContainerDockerByIDParams{Force: &force}) + if err != nil { + return nil, err + } + return &orchestrator.Result{Changed: true}, nil + }, +) +cleanup.DependsOn(execHostname, execUname) ``` -`DockerTarget` implements this by running -`docker exec /osapi provider run --data ''`. -The host's `osapi` binary is volume-mounted into the container at creation time. - ## Example See [`examples/sdk/orchestrator/features/container-targeting.go`](https://github.com/retr0h/osapi/blob/main/examples/sdk/orchestrator/features/container-targeting.go) -for a complete working example. +for a complete working example with hooks, error handling, and a deliberately +failing task. diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-create.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-create.md similarity index 92% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-create.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-create.md index 2816c9e7..eaf0b6cf 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-create.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-create.md @@ -2,7 +2,7 @@ sidebar_position: 16 --- -# container.create.execute +# docker.create.execute Create a new container from a specified image. @@ -12,7 +12,7 @@ Create a new container from a specified image. task := plan.TaskFunc("create-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { autoStart := true - resp, err := c.Container.Create(ctx, "_any", gen.ContainerCreateRequest{ + resp, err := c.Docker.Create(ctx, "_any", gen.DockerCreateRequest{ Image: "nginx:alpine", Name: ptr("my-nginx"), AutoStart: &autoStart, @@ -52,7 +52,7 @@ duplicate creation. ## Permissions -Requires `container:write` permission. +Requires `docker:write` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-exec.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-exec.md similarity index 90% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-exec.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-exec.md index 817c8232..78b740db 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-exec.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-exec.md @@ -2,7 +2,7 @@ sidebar_position: 22 --- -# container.exec.execute +# docker.exec.execute Execute a command inside a running container. @@ -11,7 +11,7 @@ Execute a command inside a running container. ```go task := plan.TaskFunc("exec-in-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - resp, err := c.Container.Exec(ctx, "_any", "my-nginx", gen.ContainerExecRequest{ + resp, err := c.Docker.Exec(ctx, "_any", "my-nginx", gen.DockerExecRequest{ Command: []string{"nginx", "-t"}, }) if err != nil { @@ -50,7 +50,7 @@ Accepts any valid target: `_any`, `_all`, a hostname, or a label selector ## Permissions -Requires `container:execute` permission. +Requires `docker:execute` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-inspect.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-inspect.md similarity index 89% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-inspect.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-inspect.md index 2dace5e7..8b7322cb 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-inspect.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-inspect.md @@ -2,7 +2,7 @@ sidebar_position: 18 --- -# container.inspect.get +# docker.inspect.get Retrieve detailed information about a specific container. @@ -11,7 +11,7 @@ Retrieve detailed information about a specific container. ```go task := plan.TaskFunc("inspect-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - resp, err := c.Container.Inspect(ctx, "_any", "my-nginx") + resp, err := c.Docker.Inspect(ctx, "_any", "my-nginx") if err != nil { return nil, err } @@ -41,7 +41,7 @@ Accepts any valid target: `_any`, `_all`, a hostname, or a label selector ## Permissions -Requires `container:read` permission. +Requires `docker:read` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-list.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-list.md similarity index 85% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-list.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-list.md index 515f2763..39478a13 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-list.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-list.md @@ -2,7 +2,7 @@ sidebar_position: 17 --- -# container.list.get +# docker.list.get List containers on the target host, optionally filtered by state. @@ -11,8 +11,8 @@ List containers on the target host, optionally filtered by state. ```go task := plan.TaskFunc("list-containers", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - state := gen.GetNodeContainerParamsStateRunning - resp, err := c.Container.List(ctx, "_any", &gen.GetNodeContainerParams{ + state := gen.GetNodeContainerDockerParamsStateRunning + resp, err := c.Docker.List(ctx, "_any", &gen.GetNodeContainerDockerParams{ State: &state, }) if err != nil { @@ -41,7 +41,7 @@ Accepts any valid target: `_any`, `_all`, a hostname, or a label selector ## Permissions -Requires `container:read` permission. +Requires `docker:read` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-pull.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-pull.md similarity index 89% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-pull.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-pull.md index d1edcf6f..dcb6cd05 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-pull.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-pull.md @@ -2,7 +2,7 @@ sidebar_position: 23 --- -# container.pull.execute +# docker.pull.execute Pull a container image to the host. @@ -11,7 +11,7 @@ Pull a container image to the host. ```go task := plan.TaskFunc("pull-image", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - resp, err := c.Container.Pull(ctx, "_any", gen.ContainerPullRequest{ + resp, err := c.Docker.Pull(ctx, "_any", gen.DockerPullRequest{ Image: "nginx:alpine", }) if err != nil { @@ -45,7 +45,7 @@ background. ## Permissions -Requires `container:write` permission. +Requires `docker:write` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-remove.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-remove.md similarity index 84% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-remove.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-remove.md index 4e394622..d1066cf0 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-remove.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-remove.md @@ -2,7 +2,7 @@ sidebar_position: 21 --- -# container.remove.execute +# docker.remove.execute Remove a container from the host. @@ -12,8 +12,8 @@ Remove a container from the host. task := plan.TaskFunc("remove-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { force := true - _, err := c.Container.Remove(ctx, "_any", "my-nginx", - &gen.DeleteNodeContainerByIDParams{Force: &force}) + _, err := c.Docker.Remove(ctx, "_any", "my-nginx", + &gen.DeleteNodeContainerDockerByIDParams{Force: &force}) if err != nil { return nil, err } @@ -40,7 +40,7 @@ Accepts any valid target: `_any`, `_all`, a hostname, or a label selector ## Permissions -Requires `container:write` permission. +Requires `docker:write` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-start.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-start.md similarity index 88% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-start.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-start.md index 17ebc208..d92619cc 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-start.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-start.md @@ -2,7 +2,7 @@ sidebar_position: 19 --- -# container.start.execute +# docker.start.execute Start a stopped container. @@ -11,7 +11,7 @@ Start a stopped container. ```go task := plan.TaskFunc("start-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - _, err := c.Container.Start(ctx, "_any", "my-nginx") + _, err := c.Docker.Start(ctx, "_any", "my-nginx") if err != nil { return nil, err } @@ -38,7 +38,7 @@ to check state first. ## Permissions -Requires `container:write` permission. +Requires `docker:write` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/operations/container-stop.md b/docs/docs/sidebar/sdk/orchestrator/operations/docker-stop.md similarity index 88% rename from docs/docs/sidebar/sdk/orchestrator/operations/container-stop.md rename to docs/docs/sidebar/sdk/orchestrator/operations/docker-stop.md index 6fca070f..2f6dfee1 100644 --- a/docs/docs/sidebar/sdk/orchestrator/operations/container-stop.md +++ b/docs/docs/sidebar/sdk/orchestrator/operations/docker-stop.md @@ -2,7 +2,7 @@ sidebar_position: 20 --- -# container.stop.execute +# docker.stop.execute Stop a running container. @@ -12,7 +12,7 @@ Stop a running container. task := plan.TaskFunc("stop-container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { timeout := 30 - _, err := c.Container.Stop(ctx, "_any", "my-nginx", gen.ContainerStopRequest{ + _, err := c.Docker.Stop(ctx, "_any", "my-nginx", gen.DockerStopRequest{ Timeout: &timeout, }) if err != nil { @@ -41,7 +41,7 @@ Accepts any valid target: `_any`, `_all`, a hostname, or a label selector ## Permissions -Requires `container:write` permission. +Requires `docker:write` permission. ## Example diff --git a/docs/docs/sidebar/sdk/orchestrator/orchestrator.md b/docs/docs/sidebar/sdk/orchestrator/orchestrator.md index db8aa61c..9111340c 100644 --- a/docs/docs/sidebar/sdk/orchestrator/orchestrator.md +++ b/docs/docs/sidebar/sdk/orchestrator/orchestrator.md @@ -40,30 +40,30 @@ report, err := plan.Run(context.Background()) Operations are the building blocks of orchestration plans. Each operation maps to an OSAPI job type that agents execute. -| Operation | Description | Idempotent | Category | -| ------------------------------------------------------------ | ---------------------- | ---------- | --------- | -| [`command.exec.execute`](operations/command-exec.md) | Execute a command | No | Command | -| [`command.shell.execute`](operations/command-shell.md) | Execute a shell string | No | Command | -| [`file.deploy.execute`](operations/file-deploy.md) | Deploy file to agent | Yes | File | -| [`file.status.get`](operations/file-status.md) | Check file status | Read-only | File | -| [`file.upload`](operations/file-upload.md) | Upload to Object Store | Yes | File | -| [`network.dns.get`](operations/network-dns-get.md) | Get DNS configuration | Read-only | Network | -| [`network.dns.update`](operations/network-dns-update.md) | Update DNS servers | Yes | Network | -| [`network.ping.do`](operations/network-ping.md) | Ping a host | Read-only | Network | -| [`node.hostname.get`](operations/node-hostname.md) | Get system hostname | Read-only | Node | -| [`node.status.get`](operations/node-status.md) | Get node status | Read-only | Node | -| [`node.disk.get`](operations/node-disk.md) | Get disk usage | Read-only | Node | -| [`node.memory.get`](operations/node-memory.md) | Get memory stats | Read-only | Node | -| [`node.uptime.get`](operations/node-uptime.md) | Get system uptime | Read-only | Node | -| [`node.load.get`](operations/node-load.md) | Get load averages | Read-only | Node | -| [`container.create.execute`](operations/container-create.md) | Create a container | No | Container | -| [`container.list.get`](operations/container-list.md) | List containers | Read-only | Container | -| [`container.inspect.get`](operations/container-inspect.md) | Inspect a container | Read-only | Container | -| [`container.start.execute`](operations/container-start.md) | Start a container | No | Container | -| [`container.stop.execute`](operations/container-stop.md) | Stop a container | No | Container | -| [`container.remove.execute`](operations/container-remove.md) | Remove a container | No | Container | -| [`container.exec.execute`](operations/container-exec.md) | Exec in a container | No | Container | -| [`container.pull.execute`](operations/container-pull.md) | Pull a container image | No | Container | +| Operation | Description | Idempotent | Category | +| -------------------------------------------------------- | ---------------------- | ---------- | -------- | +| [`command.exec.execute`](operations/command-exec.md) | Execute a command | No | Command | +| [`command.shell.execute`](operations/command-shell.md) | Execute a shell string | No | Command | +| [`file.deploy.execute`](operations/file-deploy.md) | Deploy file to agent | Yes | File | +| [`file.status.get`](operations/file-status.md) | Check file status | Read-only | File | +| [`file.upload`](operations/file-upload.md) | Upload to Object Store | Yes | File | +| [`network.dns.get`](operations/network-dns-get.md) | Get DNS configuration | Read-only | Network | +| [`network.dns.update`](operations/network-dns-update.md) | Update DNS servers | Yes | Network | +| [`network.ping.do`](operations/network-ping.md) | Ping a host | Read-only | Network | +| [`node.hostname.get`](operations/node-hostname.md) | Get system hostname | Read-only | Node | +| [`node.status.get`](operations/node-status.md) | Get node status | Read-only | Node | +| [`node.disk.get`](operations/node-disk.md) | Get disk usage | Read-only | Node | +| [`node.memory.get`](operations/node-memory.md) | Get memory stats | Read-only | Node | +| [`node.uptime.get`](operations/node-uptime.md) | Get system uptime | Read-only | Node | +| [`node.load.get`](operations/node-load.md) | Get load averages | Read-only | Node | +| [`docker.create.execute`](operations/docker-create.md) | Create a container | No | Docker | +| [`docker.list.get`](operations/docker-list.md) | List containers | Read-only | Docker | +| [`docker.inspect.get`](operations/docker-inspect.md) | Inspect a container | Read-only | Docker | +| [`docker.start.execute`](operations/docker-start.md) | Start a container | No | Docker | +| [`docker.stop.execute`](operations/docker-stop.md) | Stop a container | No | Docker | +| [`docker.remove.execute`](operations/docker-remove.md) | Remove a container | No | Docker | +| [`docker.exec.execute`](operations/docker-exec.md) | Exec in a container | No | Docker | +| [`docker.pull.execute`](operations/docker-pull.md) | Pull a container image | No | Docker | ### Idempotency diff --git a/docs/docs/sidebar/usage/cli/client/container/container.mdx b/docs/docs/sidebar/usage/cli/client/container/container.mdx index 90020231..490b0fe8 100644 --- a/docs/docs/sidebar/usage/cli/client/container/container.mdx +++ b/docs/docs/sidebar/usage/cli/client/container/container.mdx @@ -1,7 +1,6 @@ # Container -CLI for managing containers on target nodes -- create, list, inspect, start, -stop, remove, exec, and pull. +Container runtime management. Select a runtime below. import DocCardList from '@theme/DocCardList'; diff --git a/docs/docs/sidebar/usage/cli/client/container/create.md b/docs/docs/sidebar/usage/cli/client/container/docker/create.md similarity index 87% rename from docs/docs/sidebar/usage/cli/client/container/create.md rename to docs/docs/sidebar/usage/cli/client/container/docker/create.md index a6b739b4..b0f171d5 100644 --- a/docs/docs/sidebar/usage/cli/client/container/create.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/create.md @@ -3,7 +3,7 @@ Create a new container on the target node from the specified image: ```bash -$ osapi client container create --image nginx:latest +$ osapi client container docker create --image nginx:latest Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -18,7 +18,7 @@ Create a named container with environment variables, port mappings, and volume mounts: ```bash -$ osapi client container create \ +$ osapi client container docker create \ --image nginx:latest \ --name my-nginx \ --env "PORT=8080" --env "DEBUG=true" \ @@ -29,7 +29,7 @@ $ osapi client container create \ Create a container without starting it immediately: ```bash -$ osapi client container create \ +$ osapi client container docker create \ --image alpine:latest \ --name my-alpine \ --auto-start=false @@ -38,7 +38,7 @@ $ osapi client container create \ Target a specific host: ```bash -$ osapi client container create \ +$ osapi client container docker create \ --image redis:7 \ --name cache \ --target web-01 @@ -49,7 +49,7 @@ $ osapi client container create \ Use `--json` to get the full API response: ```bash -$ osapi client container create --image nginx:latest --json +$ osapi client container docker create --image nginx:latest --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/docker/docker.mdx b/docs/docs/sidebar/usage/cli/client/container/docker/docker.mdx new file mode 100644 index 00000000..f83429de --- /dev/null +++ b/docs/docs/sidebar/usage/cli/client/container/docker/docker.mdx @@ -0,0 +1,8 @@ +# Docker + +CLI for managing Docker containers on target nodes -- create, list, inspect, +start, stop, remove, exec, and pull. + +import DocCardList from '@theme/DocCardList'; + + diff --git a/docs/docs/sidebar/usage/cli/client/container/exec.md b/docs/docs/sidebar/usage/cli/client/container/docker/exec.md similarity index 86% rename from docs/docs/sidebar/usage/cli/client/container/exec.md rename to docs/docs/sidebar/usage/cli/client/container/docker/exec.md index 5052e2ba..f7ea6f95 100644 --- a/docs/docs/sidebar/usage/cli/client/container/exec.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/exec.md @@ -3,7 +3,7 @@ Execute a command inside a running container on the target node: ```bash -$ osapi client container exec --id my-nginx --command "ls,-la,/" +$ osapi client container docker exec --id my-nginx --command "ls,-la,/" Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -18,7 +18,7 @@ $ osapi client container exec --id my-nginx --command "ls,-la,/" Execute with environment variables and a working directory: ```bash -$ osapi client container exec \ +$ osapi client container docker exec \ --id my-app \ --command "python,-c,import os; print(os.environ['MY_VAR'])" \ --env "MY_VAR=hello" \ @@ -28,7 +28,7 @@ $ osapi client container exec \ Target a specific host: ```bash -$ osapi client container exec \ +$ osapi client container docker exec \ --id my-nginx \ --command "nginx,-t" \ --target web-01 @@ -39,7 +39,7 @@ $ osapi client container exec \ Use `--json` to get the full API response: ```bash -$ osapi client container exec --id my-nginx --command "ls" --json +$ osapi client container docker exec --id my-nginx --command "ls" --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/inspect.md b/docs/docs/sidebar/usage/cli/client/container/docker/inspect.md similarity index 81% rename from docs/docs/sidebar/usage/cli/client/container/inspect.md rename to docs/docs/sidebar/usage/cli/client/container/docker/inspect.md index 103fc450..64075e86 100644 --- a/docs/docs/sidebar/usage/cli/client/container/inspect.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/inspect.md @@ -3,7 +3,7 @@ Get detailed information about a specific container: ```bash -$ osapi client container inspect --id a1b2c3d4e5f6 +$ osapi client container docker inspect --id a1b2c3d4e5f6 Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -22,13 +22,13 @@ $ osapi client container inspect --id a1b2c3d4e5f6 Inspect a container by name: ```bash -$ osapi client container inspect --id my-nginx +$ osapi client container docker inspect --id my-nginx ``` Target a specific host: ```bash -$ osapi client container inspect --id my-nginx --target web-01 +$ osapi client container docker inspect --id my-nginx --target web-01 ``` ## JSON Output @@ -36,7 +36,7 @@ $ osapi client container inspect --id my-nginx --target web-01 Use `--json` to get the full API response: ```bash -$ osapi client container inspect --id a1b2c3d4e5f6 --json +$ osapi client container docker inspect --id a1b2c3d4e5f6 --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/list.md b/docs/docs/sidebar/usage/cli/client/container/docker/list.md similarity index 77% rename from docs/docs/sidebar/usage/cli/client/container/list.md rename to docs/docs/sidebar/usage/cli/client/container/docker/list.md index 4ecb5af4..e2eaf08b 100644 --- a/docs/docs/sidebar/usage/cli/client/container/list.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/list.md @@ -3,7 +3,7 @@ List containers on the target node: ```bash -$ osapi client container list +$ osapi client container docker list Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -17,21 +17,21 @@ $ osapi client container list Filter by state: ```bash -$ osapi client container list --state running -$ osapi client container list --state stopped -$ osapi client container list --state all +$ osapi client container docker list --state running +$ osapi client container docker list --state stopped +$ osapi client container docker list --state all ``` Limit the number of results: ```bash -$ osapi client container list --limit 5 +$ osapi client container docker list --limit 5 ``` Target a specific host: ```bash -$ osapi client container list --target web-01 +$ osapi client container docker list --target web-01 ``` ## JSON Output @@ -39,7 +39,7 @@ $ osapi client container list --target web-01 Use `--json` to get the full API response: ```bash -$ osapi client container list --json +$ osapi client container docker list --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/pull.md b/docs/docs/sidebar/usage/cli/client/container/docker/pull.md similarity index 72% rename from docs/docs/sidebar/usage/cli/client/container/pull.md rename to docs/docs/sidebar/usage/cli/client/container/docker/pull.md index 09eafdac..9136c20f 100644 --- a/docs/docs/sidebar/usage/cli/client/container/pull.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/pull.md @@ -3,7 +3,7 @@ Pull a container image on the target node: ```bash -$ osapi client container pull --image nginx:latest +$ osapi client container docker pull --image nginx:latest Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -16,26 +16,26 @@ $ osapi client container pull --image nginx:latest Pull a specific image version: ```bash -$ osapi client container pull --image alpine:3.18 +$ osapi client container docker pull --image alpine:3.18 ``` Pull from a custom registry: ```bash -$ osapi client container pull \ +$ osapi client container docker pull \ --image registry.example.com/myapp:v1.2.3 ``` Target a specific host: ```bash -$ osapi client container pull --image redis:7 --target web-01 +$ osapi client container docker pull --image redis:7 --target web-01 ``` Pull on all hosts: ```bash -$ osapi client container pull --image nginx:latest --target _all +$ osapi client container docker pull --image nginx:latest --target _all ``` ## JSON Output @@ -43,7 +43,7 @@ $ osapi client container pull --image nginx:latest --target _all Use `--json` to get the full API response: ```bash -$ osapi client container pull --image nginx:latest --json +$ osapi client container docker pull --image nginx:latest --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/remove.md b/docs/docs/sidebar/usage/cli/client/container/docker/remove.md similarity index 77% rename from docs/docs/sidebar/usage/cli/client/container/remove.md rename to docs/docs/sidebar/usage/cli/client/container/docker/remove.md index 61b17458..32aa2cef 100644 --- a/docs/docs/sidebar/usage/cli/client/container/remove.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/remove.md @@ -3,7 +3,7 @@ Remove a container from the target node: ```bash -$ osapi client container remove --id a1b2c3d4e5f6 +$ osapi client container docker remove --id a1b2c3d4e5f6 Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -14,13 +14,13 @@ $ osapi client container remove --id a1b2c3d4e5f6 Force removal of a running container: ```bash -$ osapi client container remove --id my-nginx --force +$ osapi client container docker remove --id my-nginx --force ``` Target a specific host: ```bash -$ osapi client container remove --id my-nginx --target web-01 +$ osapi client container docker remove --id my-nginx --target web-01 ``` ## JSON Output @@ -28,7 +28,7 @@ $ osapi client container remove --id my-nginx --target web-01 Use `--json` to get the full API response: ```bash -$ osapi client container remove --id a1b2c3d4e5f6 --json +$ osapi client container docker remove --id a1b2c3d4e5f6 --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/start.md b/docs/docs/sidebar/usage/cli/client/container/docker/start.md similarity index 76% rename from docs/docs/sidebar/usage/cli/client/container/start.md rename to docs/docs/sidebar/usage/cli/client/container/docker/start.md index 2c44b8fa..b3e1c8a3 100644 --- a/docs/docs/sidebar/usage/cli/client/container/start.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/start.md @@ -3,7 +3,7 @@ Start a stopped container on the target node: ```bash -$ osapi client container start --id a1b2c3d4e5f6 +$ osapi client container docker start --id a1b2c3d4e5f6 Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -14,13 +14,13 @@ $ osapi client container start --id a1b2c3d4e5f6 Start a container by name: ```bash -$ osapi client container start --id my-nginx +$ osapi client container docker start --id my-nginx ``` Target a specific host: ```bash -$ osapi client container start --id my-nginx --target web-01 +$ osapi client container docker start --id my-nginx --target web-01 ``` ## JSON Output @@ -28,7 +28,7 @@ $ osapi client container start --id my-nginx --target web-01 Use `--json` to get the full API response: ```bash -$ osapi client container start --id a1b2c3d4e5f6 --json +$ osapi client container docker start --id a1b2c3d4e5f6 --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/cli/client/container/stop.md b/docs/docs/sidebar/usage/cli/client/container/docker/stop.md similarity index 78% rename from docs/docs/sidebar/usage/cli/client/container/stop.md rename to docs/docs/sidebar/usage/cli/client/container/docker/stop.md index b7ec7e60..69dc0f6e 100644 --- a/docs/docs/sidebar/usage/cli/client/container/stop.md +++ b/docs/docs/sidebar/usage/cli/client/container/docker/stop.md @@ -3,7 +3,7 @@ Stop a running container on the target node: ```bash -$ osapi client container stop --id a1b2c3d4e5f6 +$ osapi client container docker stop --id a1b2c3d4e5f6 Job ID: 550e8400-e29b-41d4-a716-446655440000 @@ -14,13 +14,13 @@ $ osapi client container stop --id a1b2c3d4e5f6 Stop with a custom timeout (seconds to wait before killing): ```bash -$ osapi client container stop --id my-nginx --timeout 30 +$ osapi client container docker stop --id my-nginx --timeout 30 ``` Target a specific host: ```bash -$ osapi client container stop --id my-nginx --target web-01 +$ osapi client container docker stop --id my-nginx --target web-01 ``` ## JSON Output @@ -28,7 +28,7 @@ $ osapi client container stop --id my-nginx --target web-01 Use `--json` to get the full API response: ```bash -$ osapi client container stop --id a1b2c3d4e5f6 --json +$ osapi client container docker stop --id a1b2c3d4e5f6 --json ``` ## Flags diff --git a/docs/docs/sidebar/usage/configuration.md b/docs/docs/sidebar/usage/configuration.md index f9b17a78..a75970a7 100644 --- a/docs/docs/sidebar/usage/configuration.md +++ b/docs/docs/sidebar/usage/configuration.md @@ -141,11 +141,11 @@ OSAPI uses fine-grained `resource:verb` permissions for access control. Each API endpoint requires a specific permission. Built-in roles expand to a default set of permissions: -| Role | Permissions | -| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `admin` | `agent:read`, `agent:write`, `node:read`, `network:read`, `network:write`, `job:read`, `job:write`, `health:read`, `audit:read`, `command:execute`, `file:read`, `file:write`, `container:read`, `container:write`, `container:execute` | -| `write` | `agent:read`, `node:read`, `network:read`, `network:write`, `job:read`, `job:write`, `health:read`, `file:read`, `file:write`, `container:read`, `container:write` | -| `read` | `agent:read`, `node:read`, `network:read`, `job:read`, `health:read`, `file:read`, `container:read` | +| Role | Permissions | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `admin` | `agent:read`, `agent:write`, `node:read`, `network:read`, `network:write`, `job:read`, `job:write`, `health:read`, `audit:read`, `command:execute`, `file:read`, `file:write`, `docker:read`, `docker:write`, `docker:execute` | +| `write` | `agent:read`, `node:read`, `network:read`, `network:write`, `job:read`, `job:write`, `health:read`, `file:read`, `file:write`, `docker:read`, `docker:write` | +| `read` | `agent:read`, `node:read`, `network:read`, `job:read`, `health:read`, `file:read`, `docker:read` | ### Custom Roles @@ -243,7 +243,7 @@ api: # Permissions: agent:read, agent:write, node:read, network:read, # network:write, job:read, job:write, health:read, # audit:read, command:execute, file:read, file:write, - # container:read, container:write, container:execute + # docker:read, docker:write, docker:execute # roles: # ops: # permissions: diff --git a/docs/plans/2026-03-11-container-runtime-design.md b/docs/plans/2026-03-11-container-runtime-design.md index e897cec6..df415651 100644 --- a/docs/plans/2026-03-11-container-runtime-design.md +++ b/docs/plans/2026-03-11-container-runtime-design.md @@ -1,29 +1,21 @@ -# Container Runtime and Provider Execution Context +# Container Runtime Management ## Problem -OSAPI providers execute operations directly on the host OS. As we build new -providers (user management, cron, etc.), there is no way to develop, test, or -run them against an isolated environment. Additionally, there is no way to -manage containers on a host or compose workflows that span both the host and -containers running on it. +OSAPI manages Linux system configuration but has no way to manage containers +running on a host. As containerized workloads become standard, operators need to +create, start, stop, inspect, and execute commands in containers through the +same API and CLI they use for everything else. We need: 1. Container lifecycle management (Docker first, LXD/Podman later) as a new API domain -2. A mechanism to run existing providers inside a container without rewriting - them -3. An orchestrator DSL layer to compose host and container operations in a - single plan ## Decision Add a `container` API domain with a pluggable runtime driver interface. Docker -is the first implementation using the Go SDK. Introduce a `provider run` CLI -subcommand that executes a single provider operation as a standalone process -with JSON I/O. The orchestrator DSL uses `docker exec` + `provider run` to -transparently run providers inside containers. +is the first implementation using the Go SDK. ## Architecture @@ -193,106 +185,6 @@ The `Server` struct does not store handler references as fields. Handlers are constructed via `GetXxxHandler()` methods and returned as closures, consistent with all existing domains. -### Provider Run Subcommand - -A new `osapi provider run` CLI subcommand executes a single provider operation -as a standalone process with JSON I/O. This is the bridge that allows providers -to run inside containers. - -``` -osapi provider run --data '' -``` - -This subcommand is hidden from `--help` output. It is a machine interface -consumed by the orchestrator's Docker exec layer, not a user-facing command. - -**Behavior:** - -1. Parse provider name, operation, and JSON data from flags -2. Look up the provider in a runtime registry -3. Deserialize JSON into the typed parameter struct -4. Instantiate the provider using the local platform factory -5. Call the operation method -6. Serialize result to JSON on stdout -7. Exit 0 on success, non-zero on failure (error message as JSON on stderr) - -**Provider registry:** - -```go -type Registration struct { - Name string - Operations map[string]OperationSpec -} - -type OperationSpec struct { - NewParams func() any - Run func(ctx context.Context, params any) (any, error) -} -``` - -Each provider registers its operations and parameter types. The `provider run` -command looks up the provider and operation, creates the param type, unmarshals -JSON into it, calls `Run`, and marshals the result. - -The SDK already has typed methods and parameter structs. The `provider run` -subcommand only needs JSON-in/JSON-out because the SDK handles type safety on -the caller side. - -### Orchestrator DSL - -The orchestrator DSL adds container targeting through `Docker()` and `In()` -methods on the `Plan` instance: - -```go -p := orchestrator.NewPlan(client) -web := p.Docker("web-server", "ubuntu:24.04") - -create := p.TaskFunc("create container", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - return c.Container.Create(ctx, target, container.CreateParams{ - Image: "ubuntu:24.04", - Name: "web-server", - }) -}) - -p.In(web).TaskFunc("add user", func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) { - // c here is container-scoped — calls route through docker exec - return c.User.Create(ctx, user.CreateParams{ - Username: "deploy", - Shell: "/bin/bash", - }) -}).DependsOn(create) -``` - -**How `In(target)` works:** - -1. `p.Docker(name, image)` returns a `RuntimeTarget` handle that knows it is a - Docker container with that name and image -2. At container creation, the orchestrator volume-mounts the host's `osapi` - binary into the container -3. `p.In(target)` returns a scoped plan context where SDK client method calls - are intercepted -4. Instead of HTTP requests to the API server, the scoped client serializes - params to JSON and executes - `docker exec /osapi provider run --data ''` - through the Docker driver's `Exec` method -5. JSON stdout is deserialized back into the typed result struct - -The developer works with the same typed SDK methods. The transport changes from -HTTP to Docker exec + provider run, but the interface is identical. - -**`RuntimeTarget` interface** (for future LXD/Podman support): - -```go -type RuntimeTarget interface { - Name() string - Runtime() string // "docker", "lxd", "podman" - ExecProvider(ctx context.Context, provider, operation string, data []byte) ([]byte, error) -} -``` - -`p.Docker()` and (future) `p.LXD()` return different implementations of the same -interface. `p.In()` accepts any `RuntimeTarget`. - ### Configuration No new configuration sections are needed in `osapi.yaml`. The Docker driver @@ -338,7 +230,6 @@ internal/api/ └── handler_public_test.go # +TestGetContainerHandler cmd/ -├── provider_run.go # provider run subcommand (hidden) ├── client_container.go # parent command ├── client_container_create.go # CLI per endpoint ├── client_container_list.go @@ -349,12 +240,7 @@ cmd/ ├── client_container_exec.go └── client_container_pull.go -pkg/sdk/ -├── client/container.go # SDK service wrapper -└── orchestrator/ - ├── runtime_target.go # RuntimeTarget interface - ├── docker_target.go # Docker implementation - └── plan_in.go # In() scoped context +pkg/sdk/client/container.go # SDK service wrapper ``` ### Documentation @@ -388,10 +274,21 @@ just go::vet # lint passes | API nesting | Under `/node/{hostname}` | Containers run on a node, consistent with existing API conventions | | Separate `execute` permission | `container:execute` | Running commands in containers is a distinct privilege from lifecycle ops | | Graceful absence | Nil provider, descriptive error | Agents without Docker still work for all other providers | -| Provider run subcommand | JSON-in/JSON-out, hidden | Machine interface consumed by SDK; type safety lives in SDK | -| Provider registry | Runtime registration | No code generation needed; new providers just register themselves | -| Volume-mount binary | Mount host osapi into container | Any base image works; no custom OSAPI container image required | -| DSL via `In(target)` | Scoped client, same SDK types | Developers use identical API; only transport changes | | `{id}` parameter | String with pattern, not UUID | Docker IDs are hex strings/names, not UUIDs | | Remove force flag | Query parameter, no body | Consistent with existing DELETE endpoints | | Pull behavior | Async via job system | Large pulls can take minutes; blocking HTTP is unreliable | + +## What Was Removed + +The original design included two additional layers that have been dropped: + +- **`provider run` CLI subcommand** — A hidden command to run OSAPI providers + inside containers via `docker exec`. Removed because we are not running OSAPI + inside containers. +- **Orchestrator DSL `In(target)` / `Docker()`** — Scoped plan context that + intercepted SDK client calls and routed them through `docker exec` + + `provider run`. Removed because it depended on `provider run`. + +Container operations are managed through the standard API/CLI/SDK path, the same +as every other OSAPI domain. The orchestrator can compose container operations +with host operations using `TaskFunc` — no special DSL extensions needed. diff --git a/docs/plans/2026-03-11-container-runtime.md b/docs/plans/2026-03-11-container-runtime.md index 77fc6b6b..c37c3229 100644 --- a/docs/plans/2026-03-11-container-runtime.md +++ b/docs/plans/2026-03-11-container-runtime.md @@ -4,21 +4,17 @@ > (if subagents available) or superpowers:executing-plans to implement this > plan. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** Add container lifecycle management (Docker), a `provider run` -subcommand for running providers inside containers, and an orchestrator DSL -layer for composing host and container operations. +**Goal:** Add container lifecycle management (Docker) as a new API domain with +create, start, stop, remove, list, inspect, exec, and pull operations. **Architecture:** A `runtime.Driver` interface abstracts container runtimes with Docker as the first implementation via the Go SDK. Container lifecycle is -exposed as a new API domain under `/node/{hostname}/container`. A hidden -`provider run` CLI subcommand executes individual provider operations with JSON -I/O, enabling the orchestrator DSL's `In(target)` to transparently run providers -inside containers via `docker exec`. +exposed as a new API domain under `/node/{hostname}/container`. **Tech Stack:** Go, Docker Go SDK (`github.com/docker/docker/client`), oapi-codegen, Echo, testify/suite, NATS JetStream -**Spec:** `docs/superpowers/specs/2026-03-11-container-runtime-design.md` +**Spec:** `docs/plans/2026-03-11-container-runtime-design.md` --- @@ -1363,554 +1359,9 @@ git commit -m "feat(container): add container CLI commands" --- -## Chunk 6: Provider Run Subcommand +## Chunk 6: Documentation and Verification -### Task 13: Provider Registry - -**Files:** - -- Create: `internal/provider/registry/registry.go` -- Create: `internal/provider/registry/registry_public_test.go` - -- [ ] **Step 1: Write failing tests** - -Test: register a provider, look it up, look up nonexistent provider. - -```go -package registry_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/internal/provider/registry" -) - -type RegistryPublicTestSuite struct { - suite.Suite -} - -func (s *RegistryPublicTestSuite) TestLookup() { - tests := []struct { - name string - provider string - operation string - register bool - expectFound bool - }{ - { - name: "registered provider found", - provider: "host", - operation: "hostname", - register: true, - expectFound: true, - }, - { - name: "unregistered provider not found", - provider: "nonexistent", - operation: "get", - register: false, - expectFound: false, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - r := registry.New() - if tt.register { - r.Register(registry.Registration{ - Name: tt.provider, - Operations: map[string]registry.OperationSpec{ - tt.operation: { - NewParams: func() any { return nil }, - Run: func(_ context.Context, _ any) (any, error) { - return "result", nil - }, - }, - }, - }) - } - spec, ok := r.Lookup(tt.provider, tt.operation) - s.Equal(tt.expectFound, ok) - if tt.expectFound { - s.NotNil(spec) - } - }) - } -} - -func TestRegistryPublicTestSuite(t *testing.T) { - suite.Run(t, new(RegistryPublicTestSuite)) -} -``` - -- [ ] **Step 2: Run test to verify it fails** - -Run: -`go test -run TestRegistryPublicTestSuite -v ./internal/provider/registry/...` -Expected: FAIL - -- [ ] **Step 3: Implement the registry** - -```go -// Package registry provides a runtime registry for provider operations. -package registry - -import "context" - -// OperationSpec defines how to create params and run an operation. -type OperationSpec struct { - NewParams func() any - Run func(ctx context.Context, params any) (any, error) -} - -// Registration describes a provider and its operations. -type Registration struct { - Name string - Operations map[string]OperationSpec -} - -// Registry holds provider registrations. -type Registry struct { - providers map[string]Registration -} - -// New creates a new empty registry. -func New() *Registry { - return &Registry{ - providers: make(map[string]Registration), - } -} - -// Register adds a provider registration. -func (r *Registry) Register( - reg Registration, -) { - r.providers[reg.Name] = reg -} - -// Lookup finds an operation spec by provider and operation name. -func (r *Registry) Lookup( - provider string, - operation string, -) (*OperationSpec, bool) { - reg, ok := r.providers[provider] - if !ok { - return nil, false - } - - spec, ok := reg.Operations[operation] - if !ok { - return nil, false - } - - return &spec, true -} -``` - -- [ ] **Step 4: Run test to verify it passes** - -Run: -`go test -run TestRegistryPublicTestSuite -v ./internal/provider/registry/...` -Expected: PASS - -- [ ] **Step 5: Commit** - -```bash -git add internal/provider/registry/ -git commit -m "feat(container): add provider runtime registry" -``` - ---- - -### Task 14: Provider Run CLI Subcommand - -**Files:** - -- Create: `cmd/provider_run.go` - -- [ ] **Step 1: Write the provider run command** - -Create `cmd/provider_run.go`. This is a hidden command (`Hidden: true` on the -Cobra command). It: - -1. Takes positional args: `provider` and `operation` -2. Takes `--data` flag for JSON input -3. Builds a registry, registers all known providers -4. Looks up the provider/operation -5. Unmarshals JSON into params via `spec.NewParams()` -6. Calls `spec.Run()` -7. Marshals result to JSON on stdout -8. Exits with code 0 on success, 1 on failure - -```go -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "os" - - "github.com/spf13/cobra" - - "github.com/retr0h/osapi/internal/provider/registry" -) - -var providerRunData string - -var providerRunCmd = &cobra.Command{ - Use: "run [provider] [operation]", - Short: "Run a provider operation (internal)", - Hidden: true, - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - providerName := args[0] - operationName := args[1] - - reg := buildProviderRegistry() - spec, ok := reg.Lookup(providerName, operationName) - if !ok { - errJSON, _ := json.Marshal(map[string]string{ - "error": fmt.Sprintf("unknown provider/operation: %s/%s", providerName, operationName), - }) - fmt.Fprintln(os.Stderr, string(errJSON)) - os.Exit(1) - } - - params := spec.NewParams() - if providerRunData != "" && params != nil { - if err := json.Unmarshal([]byte(providerRunData), params); err != nil { - errJSON, _ := json.Marshal(map[string]string{"error": err.Error()}) - fmt.Fprintln(os.Stderr, string(errJSON)) - os.Exit(1) - } - } - - result, err := spec.Run(context.Background(), params) - if err != nil { - errJSON, _ := json.Marshal(map[string]string{"error": err.Error()}) - fmt.Fprintln(os.Stderr, string(errJSON)) - os.Exit(1) - } - - output, _ := json.Marshal(result) - fmt.Println(string(output)) - }, -} - -var providerCmd = &cobra.Command{ - Use: "provider", - Short: "Provider operations (internal)", - Hidden: true, -} - -func init() { - providerRunCmd.Flags().StringVar(&providerRunData, "data", "", "JSON input data") - providerCmd.AddCommand(providerRunCmd) - rootCmd.AddCommand(providerCmd) -} -``` - -- [ ] **Step 2: Write buildProviderRegistry function** - -This function creates a `registry.Registry` and registers all known providers -using the platform factory. Initially register just the `host` provider as a -proof of concept. More providers get registered as they are built. - -- [ ] **Step 3: Verify build** - -Run: `go build ./...` Expected: compiles - -- [ ] **Step 4: Verify hidden from help** - -Run: `go run main.go --help` Expected: `provider` does NOT appear in the command -list - -- [ ] **Step 5: Commit** - -```bash -git add cmd/provider_run.go -git commit -m "feat(container): add hidden provider run subcommand" -``` - ---- - -## Chunk 7: Orchestrator DSL - -### Task 15: RuntimeTarget Interface - -**Files:** - -- Create: `pkg/sdk/orchestrator/runtime_target.go` -- Create: `pkg/sdk/orchestrator/runtime_target_public_test.go` - -- [ ] **Step 1: Write the RuntimeTarget interface** - -```go -// Package orchestrator provides DAG-based task orchestration. -package orchestrator - -import "context" - -// RuntimeTarget represents a container runtime target that can execute -// provider operations. Implementations exist for Docker (now) and -// LXD/Podman (later). -type RuntimeTarget interface { - // Name returns the target name (container name). - Name() string - // Runtime returns the runtime type ("docker", "lxd", "podman"). - Runtime() string - // ExecProvider executes a provider operation inside the target. - ExecProvider(ctx context.Context, provider, operation string, data []byte) ([]byte, error) -} -``` - -- [ ] **Step 2: Commit** - -```bash -git add pkg/sdk/orchestrator/runtime_target.go -git commit -m "feat(container): add RuntimeTarget interface" -``` - ---- - -### Task 16: Docker RuntimeTarget Implementation - -**Files:** - -- Create: `pkg/sdk/orchestrator/docker_target.go` -- Create: `pkg/sdk/orchestrator/docker_target_public_test.go` - -- [ ] **Step 1: Write failing test** - -Test that `DockerTarget` implements `RuntimeTarget`, returns correct name and -runtime type, and that `ExecProvider` constructs the correct exec call. - -- [ ] **Step 2: Run test to verify it fails** - -Run: -`go test -run TestDockerTargetPublicTestSuite -v ./pkg/sdk/orchestrator/...` -Expected: FAIL - -- [ ] **Step 3: Implement DockerTarget** - -Note: The orchestrator lives in `pkg/sdk/` which should not import `internal/` -packages directly. Instead of importing `runtime.Driver`, we inject an `ExecFn` -function type that the caller wires to the Docker driver. - -```go -package orchestrator - -import ( - "context" - "fmt" -) - -// ExecFn executes a command inside a container and returns stdout/stderr/exit code. -// This is injected by the caller, typically wired to runtime.Driver.Exec. -type ExecFn func(ctx context.Context, containerID string, command []string) (stdout, stderr string, exitCode int, err error) - -// DockerTarget implements RuntimeTarget for Docker containers. -type DockerTarget struct { - name string - image string - execFn ExecFn -} - -// NewDockerTarget creates a new Docker runtime target. -func NewDockerTarget( - name string, - image string, - execFn ExecFn, -) *DockerTarget { - return &DockerTarget{ - name: name, - image: image, - execFn: execFn, - } -} - -// Name returns the container name. -func (t *DockerTarget) Name() string { - return t.name -} - -// Runtime returns "docker". -func (t *DockerTarget) Runtime() string { - return "docker" -} - -// Image returns the container image. -func (t *DockerTarget) Image() string { - return t.image -} - -// ExecProvider runs a provider operation inside this container via docker exec. -func (t *DockerTarget) ExecProvider( - ctx context.Context, - provider string, - operation string, - data []byte, -) ([]byte, error) { - cmd := []string{"/osapi", "provider", "run", provider, operation} - if len(data) > 0 { - cmd = append(cmd, "--data", string(data)) - } - - stdout, stderr, exitCode, err := t.execFn(ctx, t.name, cmd) - if err != nil { - return nil, fmt.Errorf("exec provider in container %s: %w", t.name, err) - } - - if exitCode != 0 { - return nil, fmt.Errorf("provider %s/%s failed (exit %d): %s", - provider, operation, exitCode, stderr) - } - - return []byte(stdout), nil -} -``` - -The caller (e.g., `cmd/` or the user's orchestrator code) wires the `ExecFn` by -closing over the Docker driver: - -```go -execFn := func(ctx context.Context, id string, cmd []string) (string, string, int, error) { - result, err := dockerDriver.Exec(ctx, id, runtime.ExecParams{Command: cmd}) - if err != nil { - return "", "", -1, err - } - return result.Stdout, result.Stderr, result.ExitCode, nil -} -target := orchestrator.NewDockerTarget("web", "ubuntu:24.04", execFn) -``` - -- [ ] **Step 4: Run test to verify it passes** - -Run: -`go test -run TestDockerTargetPublicTestSuite -v ./pkg/sdk/orchestrator/...` -Expected: PASS - -- [ ] **Step 5: Commit** - -```bash -git add pkg/sdk/orchestrator/docker_target.go \ - pkg/sdk/orchestrator/docker_target_public_test.go -git commit -m "feat(container): add Docker RuntimeTarget implementation" -``` - ---- - -### Task 17: Plan.Docker() and Plan.In() Methods - -**Files:** - -- Create: `pkg/sdk/orchestrator/plan_in.go` -- Create: `pkg/sdk/orchestrator/plan_in_public_test.go` - -- [ ] **Step 1: Write failing tests** - -Test that `p.Docker()` returns a `*DockerTarget` with correct name/image. Test -that `p.In()` returns a `ScopedPlan` and that `TaskFunc` on the scoped plan adds -a task to the parent plan. - -- [ ] **Step 2: Run test to verify it fails** - -Run: `go test -run TestPlanInPublicTestSuite -v ./pkg/sdk/orchestrator/...` -Expected: FAIL - -- [ ] **Step 3: Implement Docker() and In()** - -```go -package orchestrator - -// Docker creates a DockerTarget bound to this plan's container exec function. -// Panics if no ExecFn was provided via WithDockerExecFn option. -func (p *Plan) Docker( - name string, - image string, -) *DockerTarget { - if p.dockerExecFn == nil { - panic("orchestrator: Plan.Docker() called without WithDockerExecFn option") - } - return NewDockerTarget(name, image, p.dockerExecFn) -} - -// In returns a ScopedPlan that routes provider operations through the -// given RuntimeTarget (e.g., a Docker container). -type ScopedPlan struct { - plan *Plan - target RuntimeTarget -} - -// In creates a scoped plan context for the given runtime target. -func (p *Plan) In( - target RuntimeTarget, -) *ScopedPlan { - return &ScopedPlan{ - plan: p, - target: target, - } -} - -// TaskFunc creates a task on the parent plan that executes within the -// scoped runtime target context. -func (sp *ScopedPlan) TaskFunc( - name string, - fn TaskFn, -) *Task { - // Wrap the function to inject the runtime target context - return sp.plan.TaskFunc(name, fn) -} - -// TaskFuncWithResults creates a task with results on the parent plan. -func (sp *ScopedPlan) TaskFuncWithResults( - name string, - fn TaskFnWithResults, -) *Task { - return sp.plan.TaskFuncWithResults(name, fn) -} -``` - -Note: The full client-interception layer (where SDK calls automatically route -through `docker exec` + `provider run`) is more complex. This initial -implementation provides the `In()` scoping mechanism. The interception layer -that replaces the HTTP transport with Docker exec is the next evolution — for -now, task functions inside `In()` can manually use `target.ExecProvider()` to -run providers in the container. - -- [ ] **Step 4: Run test to verify it passes** - -Run: `go test -run TestPlanInPublicTestSuite -v ./pkg/sdk/orchestrator/...` -Expected: PASS - -- [ ] **Step 5: Add dockerExecFn field to Plan** - -Modify `pkg/sdk/orchestrator/plan.go` to add an optional `dockerExecFn ExecFn` -field to `PlanConfig` and a `WithDockerExecFn(fn ExecFn)` plan option. The -`Plan.Docker()` method reads from `p.config.dockerExecFn`. - -- [ ] **Step 6: Run full orchestrator tests** - -Run: `go test -v ./pkg/sdk/orchestrator/...` Expected: all tests pass - -- [ ] **Step 7: Commit** - -```bash -git add pkg/sdk/orchestrator/plan_in.go \ - pkg/sdk/orchestrator/plan_in_public_test.go \ - pkg/sdk/orchestrator/plan.go -git commit -m "feat(container): add Plan.Docker() and Plan.In() DSL methods" -``` - ---- - -## Chunk 8: Documentation and Verification - -### Task 18: Documentation +### Task 13: Documentation **Files:** @@ -1966,7 +1417,7 @@ git commit -m "docs: add container management documentation" --- -### Task 19: Integration Test Smoke Suite +### Task 14: Integration Test Smoke Suite **Files:** @@ -1997,7 +1448,7 @@ git commit -m "test(container): add integration test smoke suite" --- -### Task 20: Final Verification +### Task 15: Final Verification - [ ] **Step 1: Regenerate all specs and code** @@ -2026,10 +1477,10 @@ Run coverage and check that every new package has 100% line coverage: ```bash go test -race -coverprofile=.coverage/cover.out -v ./... grep -v -f .coverignore .coverage/cover.out > .coverage/cover.tmp && mv .coverage/cover.tmp .coverage/cover.out -go tool cover -func=.coverage/cover.out | grep -E 'container|registry' | grep -v '100.0%' +go tool cover -func=.coverage/cover.out | grep 'container' | grep -v '100.0%' ``` -Expected: **no output** (all container and registry packages at 100%). +Expected: **no output** (all container packages at 100%). If any lines are uncovered, add tests before proceeding. The `.coverignore` already excludes `/cmd/`, `/gen/`, `main.go`, and `/mocks/`, so handler tests in diff --git a/docs/plans/2026-03-13-container-runtime-rename-design.md b/docs/plans/2026-03-13-container-runtime-rename-design.md new file mode 100644 index 00000000..35b44359 --- /dev/null +++ b/docs/plans/2026-03-13-container-runtime-rename-design.md @@ -0,0 +1,190 @@ +# Container Runtime: Docker-Specific Domain Design + +## Problem + +The current container domain uses a generic `container` name with an +auto-detecting runtime driver. This is wrong — the user decides which runtime to +use, not the agent. Docker, LXD, and Podman are fundamentally different systems +with different concepts, options, and behaviors. A shared abstraction would be +lowest-common-denominator or leak everywhere. + +## Decision + +Rename the `container` domain to `docker`. Each future runtime (LXD, Podman) +becomes its own independent domain — no shared interface, no shared types, no +abstraction tax. + +The CLI groups runtimes under a `container` parent command for discoverability. +API paths mirror this with `/container/docker/`. + +## Architecture + +### Naming Convention + +| Layer | Current | New | +| ------------ | ------------------------------ | ----------------------------------- | +| API paths | `/node/{hostname}/container` | `/node/{hostname}/container/docker` | +| Permissions | `container:read/write/execute` | `docker:read/write/execute` | +| CLI | `client container list` | `client container docker list` | +| SDK | `client.Container.Pull()` | `client.Docker.Pull()` | +| Job category | `container` | `docker` | +| Provider pkg | `internal/provider/container/` | `internal/provider/docker/` | +| API pkg | `internal/api/container/` | `internal/api/docker/` | + +### API Endpoints + +| Method | Path | Operation | Permission | +| -------- | ---------------------------------------------- | --------- | ---------------- | +| `POST` | `/node/{hostname}/container/docker` | Create | `docker:write` | +| `GET` | `/node/{hostname}/container/docker` | List | `docker:read` | +| `GET` | `/node/{hostname}/container/docker/{id}` | Inspect | `docker:read` | +| `POST` | `/node/{hostname}/container/docker/{id}/start` | Start | `docker:write` | +| `POST` | `/node/{hostname}/container/docker/{id}/stop` | Stop | `docker:write` | +| `DELETE` | `/node/{hostname}/container/docker/{id}` | Remove | `docker:write` | +| `POST` | `/node/{hostname}/container/docker/{id}/exec` | Exec | `docker:execute` | +| `POST` | `/node/{hostname}/container/docker/pull` | Pull | `docker:write` | + +### CLI + +``` +osapi client container docker list [--target HOST] [--state STATE] [--limit N] +osapi client container docker create --target HOST --image IMAGE [--name NAME] ... +osapi client container docker inspect --target HOST --id ID +osapi client container docker start --target HOST --id ID +osapi client container docker stop --target HOST --id ID [--timeout SECONDS] +osapi client container docker remove --target HOST --id ID [--force] +osapi client container docker exec --target HOST --id ID --command CMD... +osapi client container docker pull --target HOST --image IMAGE +``` + +The `container` command is a parent with `` for grouping. Each +runtime is a subcommand. Future runtimes add `client container lxd`, +`client container podman`, etc. + +### Role Updates + +| Role | Permissions | +| ------- | ----------------------------------------------- | +| `admin` | `docker:read`, `docker:write`, `docker:execute` | +| `write` | `docker:read`, `docker:write` | +| `read` | `docker:read` | + +### Package Layout + +``` +internal/provider/docker/ +├── docker.go # Provider struct, New() +├── types.go # CreateParams, Container, etc. +├── docker_test.go # Tests +└── (no runtime/ subdirectory) + +internal/api/docker/ +├── gen/ +│ ├── api.yaml # OpenAPI spec +│ ├── cfg.yaml # oapi-codegen config +│ └── generate.go # go:generate directive +├── types.go # Domain struct, interfaces +├── docker.go # New(), interface check +├── docker_create.go # Create handler +├── docker_list.go # List handler +├── docker_inspect.go # Inspect handler +├── docker_start.go # Start handler +├── docker_stop.go # Stop handler +├── docker_remove.go # Remove handler +├── docker_exec.go # Exec handler +├── docker_pull.go # Pull handler +└── *_public_test.go # Tests + +internal/api/ +├── handler_docker.go # GetDockerHandler() method +└── handler.go # +RegisterHandlers() wiring + +cmd/ +├── client_container.go # parent: `container` subcommand +├── client_container_docker.go # parent: `docker` subcommand +├── client_container_docker_create.go +├── client_container_docker_list.go +├── client_container_docker_inspect.go +├── client_container_docker_start.go +├── client_container_docker_stop.go +├── client_container_docker_remove.go +├── client_container_docker_exec.go +└── client_container_docker_pull.go + +pkg/sdk/client/ +├── docker.go # DockerService +└── docker_types.go # DockerResult, etc. + +internal/agent/ +├── processor_docker.go # docker case + dispatch +└── types.go # dockerProvider field +``` + +### No Shared Runtime Interface + +The `runtime.Driver` interface in +`internal/provider/container/runtime/driver.go` is removed. The Docker provider +defines its own types directly. When LXD is added, it gets its own provider +package (`internal/provider/lxd/`) with its own types — LXD concepts (instances, +profiles, projects) don't map to Docker concepts (images, containers, layers). + +Each runtime is fully independent: + +- Own API domain, paths, and OpenAPI schemas +- Own CLI subcommands under `client container ` +- Own SDK service (`client.Docker`, `client.Lxd`) +- Own permissions (`docker:read`, `lxd:read`) +- Own provider package with own types +- Own orchestrator helpers + +### Orchestrator DSL + +Convenience methods on `*Plan` in `pkg/sdk/orchestrator/` wrap `client.Docker.*` +calls so users don't write boilerplate TaskFunc bodies: + +```go +plan.DockerPull("pull-image", target, "ubuntu:24.04") +plan.DockerCreate("create-app", target, gen.DockerCreateRequest{...}) +plan.DockerExec("run-cmd", target, "my-app", gen.DockerExecRequest{...}) +plan.DockerInspect("check", target, "my-app") +plan.DockerStart("start", target, "my-app") +plan.DockerStop("stop", target, "my-app", gen.DockerStopRequest{...}) +plan.DockerRemove("cleanup", target, "my-app", &gen.DeleteNodeDockerByIDParams{...}) +``` + +Each returns `*Task` for chaining (`DependsOn`, `OnlyIfChanged`, etc.). Future +runtimes add `plan.LxdLaunch(...)`, `plan.LxdExec(...)` — no shared interface. + +### Documentation + +- `docs/docs/sidebar/features/container-management.md` — update to describe + Docker as the first runtime, explain the per-runtime model +- CLI docs: restructure under `container/docker/` +- SDK orchestrator docs: update container-targeting to use `plan.DockerPull` + etc. + +## What This Changes + +This is a mechanical rename + restructure of the existing fully-built container +domain. No behavior changes. The scope is: + +1. Rename ~40+ files across all layers (API, CLI, SDK, agent, provider, job + types, tests, docs) +2. Remove `internal/provider/container/runtime/driver.go` shared interface +3. Flatten `internal/provider/container/runtime/docker/` → + `internal/provider/docker/` +4. Add `container` parent CLI command +5. Add orchestrator DSL helpers +6. Update all docs + +## Key Design Decisions + +| Decision | Choice | Rationale | +| ---------------------------- | ------------------- | ------------------------------------------------------ | +| User chooses runtime | Yes | Agent shouldn't guess; Docker/LXD/Podman are different | +| Separate domains per runtime | Yes | No useful shared abstraction across runtimes | +| CLI nesting | `container docker` | Groups runtimes for discoverability | +| API path nesting | `/container/docker` | Mirrors CLI structure | +| No shared interface | Yes | LXD concepts don't map to Docker concepts | +| Flat provider packages | Yes | No shared parent code to justify nesting | +| Orchestrator helpers | Methods on Plan | Eliminates TaskFunc boilerplate | diff --git a/docs/plans/2026-03-13-container-runtime-rename.md b/docs/plans/2026-03-13-container-runtime-rename.md new file mode 100644 index 00000000..e73ac941 --- /dev/null +++ b/docs/plans/2026-03-13-container-runtime-rename.md @@ -0,0 +1,976 @@ +# Container → Docker Domain Rename Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to +> implement this plan task-by-task. + +**Goal:** Rename the generic `container` domain to `docker`, nest under a +`container` parent in CLI/API paths, remove the shared `runtime.Driver` +interface, and add orchestrator DSL helpers. + +**Architecture:** Mechanical rename across all layers (API, provider, agent, +job, CLI, SDK, permissions, docs, tests) from `container` to `docker`. API paths +change from `/node/{hostname}/container` to `/node/{hostname}/container/docker`. +CLI changes from `client container list` to `client container docker list`. The +shared `runtime.Driver` interface is removed — Docker provider owns its types +directly. New orchestrator DSL methods (`plan.DockerPull`, etc.) wrap SDK client +calls. + +**Tech Stack:** Go, Echo, oapi-codegen, Cobra, testify/suite, Docker Go SDK + +**Spec:** `docs/plans/2026-03-13-container-runtime-rename-design.md` + +--- + +## Chunk 1: OpenAPI Spec + Permissions + +### Task 1: Rename OpenAPI Spec + +**Files:** + +- Modify: `internal/api/container/gen/api.yaml` + +**Step 1:** Rename the directory: + +```bash +git mv internal/api/container internal/api/docker +``` + +**Step 2:** Edit `internal/api/docker/gen/api.yaml`: + +- Change all paths from `/node/{hostname}/container` to + `/node/{hostname}/container/docker` +- Change all security scopes from `container:read/write/execute` to + `docker:read/write/execute` +- Rename all schema names from `Container*` to `Docker*` (e.g., + `ContainerCreateRequest` → `DockerCreateRequest`, `ContainerResponse` → + `DockerResponse`) +- Rename all operation IDs from `*Container*` to `*Docker*` +- Update tag names and descriptions from "Container" to "Docker" +- Update the `ContainerId` parameter to `DockerId` + +**Step 3:** Update `internal/api/docker/gen/cfg.yaml`: + +- Change output filename from `container.gen.go` to `docker.gen.go` +- Update package name if needed + +**Step 4:** Update `internal/api/docker/gen/generate.go`: + +- Update the `go:generate` directive path if needed + +**Step 5:** Regenerate: + +```bash +go generate ./internal/api/docker/gen/... +``` + +**Step 6:** Verify generation succeeded — `docker.gen.go` should exist: + +```bash +ls internal/api/docker/gen/docker.gen.go +``` + +**Step 7:** Delete old generated file if it still exists: + +```bash +rm -f internal/api/docker/gen/container.gen.go +``` + +**Step 8:** Commit: + +```bash +git add internal/api/docker/ internal/api/container/ +git commit -m "refactor: rename container OpenAPI spec to docker" +``` + +--- + +### Task 2: Rename Permissions + +**Files:** + +- Modify: `internal/authtoken/permissions.go` + +**Step 1:** Rename the permission constants: + +- `PermContainerRead` → `PermDockerRead` with value `"docker:read"` +- `PermContainerWrite` → `PermDockerWrite` with value `"docker:write"` +- `PermContainerExecute` → `PermDockerExecute` with value `"docker:execute"` + +**Step 2:** Update the `AllPermissions` slice to use the new names. + +**Step 3:** Update the default role mappings (admin, write, read) to use the new +permission constants. + +**Step 4:** Search for any other files referencing the old permission constants: + +```bash +grep -rn 'PermContainer\|container:read\|container:write\|container:execute' \ + --include='*.go' . | grep -v '.gen.go' | grep -v '_test.go' +``` + +Fix all references found (likely in `internal/api/docker/` handlers and +`cmd/api_helpers.go`). + +**Step 5:** Verify it compiles: + +```bash +go build ./internal/authtoken/... ./internal/api/docker/... +``` + +**Step 6:** Run permission tests: + +```bash +go test ./internal/authtoken/... -count=1 +``` + +**Step 7:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container permissions to docker" +``` + +--- + +## Chunk 2: Provider Layer + +### Task 3: Flatten and Rename Provider + +**Files:** + +- Rename: `internal/provider/container/` → `internal/provider/docker/` +- Remove: `internal/provider/container/runtime/driver.go` (shared interface) + +**Step 1:** Move the Docker driver implementation up and rename: + +```bash +git mv internal/provider/container internal/provider/docker +``` + +**Step 2:** The directory structure should become: + +``` +internal/provider/docker/ +├── provider.go +├── types.go +├── provider_public_test.go +├── mocks/ +├── runtime/ +│ ├── driver.go ← DELETE this (shared interface) +│ ├── docker/ +│ │ ├── docker.go +│ │ └── docker_public_test.go +│ └── mocks/ +``` + +Move `runtime/docker/docker.go` types and implementation into the parent +package, or keep `runtime/docker/` as the actual Docker SDK driver. The provider +in `provider.go` already wraps the driver, so the structure can stay — just +update the package import paths from `internal/provider/container/...` to +`internal/provider/docker/...`. + +**Step 3:** Delete the shared `runtime.Driver` interface: + +```bash +rm internal/provider/docker/runtime/driver.go +``` + +**Step 4:** Update the Docker driver (`runtime/docker/docker.go`) to define its +own interface or use concrete types instead of the removed `runtime.Driver`. The +provider in `provider.go` should type-assert or use the concrete Docker driver +type. + +**Step 5:** Update all import paths in the provider package from +`internal/provider/container` to `internal/provider/docker`. + +**Step 6:** Update package declarations — `package container` → `package docker` +in `provider.go`, `types.go`, etc. + +**Step 7:** Rename the `Provider` interface methods and types if they use +`Container` prefix (check `types.go`). + +**Step 8:** Update mock generation directives in `mocks/generate.go`. + +**Step 9:** Regenerate mocks: + +```bash +go generate ./internal/provider/docker/mocks/... +go generate ./internal/provider/docker/runtime/mocks/... +``` + +**Step 10:** Verify it compiles: + +```bash +go build ./internal/provider/docker/... +``` + +**Step 11:** Run tests: + +```bash +go test ./internal/provider/docker/... -count=1 +``` + +**Step 12:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container provider to docker" +``` + +--- + +## Chunk 3: Job Types + +### Task 4: Rename Job Types and Operations + +**Files:** + +- Modify: `internal/job/types.go` +- Modify: `internal/job/client/modify_container.go` (rename to + `modify_docker.go`) +- Modify: `internal/job/client/modify_container_public_test.go` (rename to + `modify_docker_public_test.go`) + +**Step 1:** In `internal/job/types.go`, rename all container operation +constants: + +- `OperationContainerCreate` → `OperationDockerCreate` with value + `"docker.create.execute"` +- `OperationContainerStart` → `OperationDockerStart` with value + `"docker.start.execute"` +- `OperationContainerStop` → `OperationDockerStop` with value + `"docker.stop.execute"` +- `OperationContainerRemove` → `OperationDockerRemove` with value + `"docker.remove.execute"` +- `OperationContainerList` → `OperationDockerList` with value + `"docker.list.get"` +- `OperationContainerInspect` → `OperationDockerInspect` with value + `"docker.inspect.get"` +- `OperationContainerExec` → `OperationDockerExec` with value + `"docker.exec.execute"` +- `OperationContainerPull` → `OperationDockerPull` with value + `"docker.pull.execute"` + +**Step 2:** Rename all data types: + +- `ContainerCreateData` → `DockerCreateData` +- `ContainerStopData` → `DockerStopData` +- `ContainerRemoveData` → `DockerRemoveData` +- `ContainerListData` → `DockerListData` +- `ContainerExecData` → `DockerExecData` +- `ContainerPullData` → `DockerPullData` + +**Step 3:** Rename the job client file: + +```bash +git mv internal/job/client/modify_container.go \ + internal/job/client/modify_docker.go +git mv internal/job/client/modify_container_public_test.go \ + internal/job/client/modify_docker_public_test.go +``` + +**Step 4:** Update function names in the renamed files (e.g., `ModifyContainer*` +→ `ModifyDocker*` or whatever the current pattern is). + +**Step 5:** Search for all remaining references to old constant/type names: + +```bash +grep -rn 'OperationContainer\|ContainerCreateData\|ContainerStopData\|ContainerRemoveData\|ContainerListData\|ContainerExecData\|ContainerPullData' \ + --include='*.go' . | grep -v '.gen.go' +``` + +Fix all references found. + +**Step 6:** Verify it compiles: + +```bash +go build ./internal/job/... +``` + +**Step 7:** Run tests: + +```bash +go test ./internal/job/... -count=1 +``` + +**Step 8:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container job types to docker" +``` + +--- + +## Chunk 4: Agent Layer + +### Task 5: Rename Agent Processor and Wiring + +**Files:** + +- Rename: `internal/agent/processor_container.go` → + `internal/agent/processor_docker.go` +- Rename: `internal/agent/processor_container_test.go` → + `internal/agent/processor_docker_test.go` +- Modify: `internal/agent/types.go` +- Modify: `internal/agent/agent.go` +- Modify: `internal/agent/factory.go` +- Modify: `internal/agent/processor.go` +- Modify: `internal/agent/factory_test.go` +- Modify: `internal/agent/factory_public_test.go` + +**Step 1:** Rename processor files: + +```bash +git mv internal/agent/processor_container.go \ + internal/agent/processor_docker.go +git mv internal/agent/processor_container_test.go \ + internal/agent/processor_docker_test.go +``` + +**Step 2:** In `processor_docker.go`: + +- Rename `processContainerOperation` → `processDockerOperation` +- Rename `processContainerCreate` → `processDockerCreate` (and all 8 process + methods) +- Change `a.containerProvider` → `a.dockerProvider` +- Update import from `internal/provider/container` to `internal/provider/docker` + +**Step 3:** In `processor.go`: + +- Change `case "container":` → `case "docker":` +- Change `a.processContainerOperation` → `a.processDockerOperation` + +**Step 4:** In `types.go`: + +- Change `containerProvider containerProv.Provider` → + `dockerProvider dockerProv.Provider` +- Update the import alias from `containerProv` to `dockerProv` + +**Step 5:** In `agent.go`: + +- Update parameter name from `containerProvider` to `dockerProvider` +- Update field assignment + +**Step 6:** In `factory.go`: + +- Rename `containerProvider` variable to `dockerProvider` +- Update import from `internal/provider/container` to `internal/provider/docker` +- Update the return value + +**Step 7:** In `factory_test.go` and `factory_public_test.go`: + +- Update variable names and comments + +**Step 8:** In `processor_docker_test.go`: + +- Update all function names and references + +**Step 9:** Verify it compiles: + +```bash +go build ./internal/agent/... +``` + +**Step 10:** Run tests: + +```bash +go test ./internal/agent/... -count=1 +``` + +**Step 11:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container agent wiring to docker" +``` + +--- + +## Chunk 5: API Handlers + +### Task 6: Rename API Handler Files and Wiring + +**Files:** + +- Modify: all files in `internal/api/docker/` (already moved in Task 1) +- Rename: `internal/api/handler_container.go` → `internal/api/handler_docker.go` +- Modify: `internal/api/handler.go` +- Modify: `internal/api/types.go` +- Modify: `internal/api/handler_public_test.go` +- Modify: `cmd/api_helpers.go` + +**Step 1:** In `internal/api/docker/`: + +- Rename all files: `container_create.go` → `docker_create.go`, etc. (8 handler + files + 8 test files) +- Update package declaration from `package container` to `package docker` +- Rename the `Container` struct to `Docker` +- Update `New()` to return `*Docker` +- Rename handler methods (e.g., `PostNodeContainer` → `PostNodeContainerDocker`) +- Update all gen import aliases from `containerGen` to `dockerGen` +- Update compile-time interface check +- Update references to old job operation constants +- Update references to old data types + +**Step 2:** Rename and update `internal/api/docker/types.go`: + +- Rename the struct and any interfaces + +**Step 3:** Rename and update `internal/api/docker/convert.go`: + +- Update function names and types + +**Step 4:** Rename and update `internal/api/docker/validate.go`: + +- Update function names + +**Step 5:** Rename server wiring: + +```bash +git mv internal/api/handler_container.go internal/api/handler_docker.go +``` + +**Step 6:** In `handler_docker.go`: + +- Rename `GetContainerHandler` → `GetDockerHandler` +- Update imports from `internal/api/container` to `internal/api/docker` +- Update scope references from `container:*` to `docker:*` + +**Step 7:** In `types.go`, rename the handler field and option function. + +**Step 8:** In `handler.go`, update the `RegisterHandlers` call. + +**Step 9:** In `handler_public_test.go`, rename `TestGetContainerHandler` → +`TestGetDockerHandler`. + +**Step 10:** In `cmd/api_helpers.go`, update `GetContainerHandler` → +`GetDockerHandler`. + +**Step 11:** Regenerate the combined spec and SDK client: + +```bash +just generate +go generate ./pkg/sdk/client/gen/... +``` + +**Step 12:** Verify it compiles: + +```bash +go build ./... +``` + +**Step 13:** Run tests: + +```bash +go test ./internal/api/docker/... ./internal/api/... -count=1 +``` + +**Step 14:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container API handlers to docker" +``` + +--- + +## Chunk 6: CLI + +### Task 7: Restructure CLI Commands + +**Files:** + +- Modify: `cmd/client_container.go` (becomes parent with just `` + grouping) +- Create: `cmd/client_container_docker.go` (new `docker` subcommand) +- Rename: all `cmd/client_container_*.go` → `cmd/client_container_docker_*.go` + +**Step 1:** Update `cmd/client_container.go` to be a thin parent command: + +```go +var clientContainerCmd = &cobra.Command{ + Use: "container", + Short: "Container runtime management", + Long: `Manage containers using runtime-specific subcommands.`, +} + +func init() { + clientCmd.AddCommand(clientContainerCmd) +} +``` + +**Step 2:** Create `cmd/client_container_docker.go`: + +```go +var clientContainerDockerCmd = &cobra.Command{ + Use: "docker", + Short: "Docker container operations", + Long: `Manage Docker containers on target nodes.`, +} + +func init() { + clientContainerCmd.AddCommand(clientContainerDockerCmd) +} +``` + +**Step 3:** Rename all subcommand files: + +```bash +git mv cmd/client_container_create.go cmd/client_container_docker_create.go +git mv cmd/client_container_list.go cmd/client_container_docker_list.go +git mv cmd/client_container_inspect.go cmd/client_container_docker_inspect.go +git mv cmd/client_container_start.go cmd/client_container_docker_start.go +git mv cmd/client_container_stop.go cmd/client_container_docker_stop.go +git mv cmd/client_container_remove.go cmd/client_container_docker_remove.go +git mv cmd/client_container_exec.go cmd/client_container_docker_exec.go +git mv cmd/client_container_pull.go cmd/client_container_docker_pull.go +``` + +**Step 4:** In each renamed file: + +- Change parent command registration from `clientContainerCmd.AddCommand(...)` + to `clientContainerDockerCmd.AddCommand(...)` +- Rename cobra command variables from `clientContainer*Cmd` to + `clientContainerDocker*Cmd` +- Update SDK client calls from `c.Container.*` to `c.Docker.*` +- Update generated type references from `gen.Container*` to `gen.Docker*` +- Update `gen.GetNodeContainerParams*` to `gen.GetNodeDockerParams*` (or + whatever the regenerated names are) + +**Step 5:** Verify the CLI compiles: + +```bash +go build ./cmd/... +``` + +**Step 6:** Verify the command tree looks right: + +```bash +go run main.go client container --help +go run main.go client container docker --help +``` + +**Step 7:** Commit: + +```bash +git add -A && git commit -m "refactor: nest docker CLI under client container docker" +``` + +--- + +## Chunk 7: SDK Client + +### Task 8: Rename SDK Client Service + +**Files:** + +- Rename: `pkg/sdk/client/container.go` → `pkg/sdk/client/docker.go` +- Rename: `pkg/sdk/client/container_types.go` → `pkg/sdk/client/docker_types.go` +- Rename: `pkg/sdk/client/container_public_test.go` → + `pkg/sdk/client/docker_public_test.go` +- Rename: `pkg/sdk/client/container_types_test.go` → + `pkg/sdk/client/docker_types_test.go` +- Modify: `pkg/sdk/client/osapi.go` + +**Step 1:** Rename files: + +```bash +git mv pkg/sdk/client/container.go pkg/sdk/client/docker.go +git mv pkg/sdk/client/container_types.go pkg/sdk/client/docker_types.go +git mv pkg/sdk/client/container_public_test.go \ + pkg/sdk/client/docker_public_test.go +git mv pkg/sdk/client/container_types_test.go \ + pkg/sdk/client/docker_types_test.go +``` + +**Step 2:** In `docker.go`: + +- Rename `ContainerService` → `DockerService` +- Update all method bodies to use the regenerated `gen.Docker*` type names +- Update error message prefixes + +**Step 3:** In `docker_types.go`: + +- Rename all types: `ContainerResult` → `DockerResult`, `ContainerListResult` → + `DockerListResult`, etc. +- Rename all converter functions: `containerResultCollectionFromGen` → + `dockerResultCollectionFromGen`, etc. + +**Step 4:** In `osapi.go`: + +- Change field `Container *ContainerService` → `Docker *DockerService` +- Update initialization: `c.Container = &ContainerService{...}` → + `c.Docker = &DockerService{...}` +- Update comment + +**Step 5:** In test files, update all type and method references. + +**Step 6:** Search for remaining `Container` references in the SDK: + +```bash +grep -rn 'Container' pkg/sdk/client/ --include='*.go' | grep -v '.gen.go' +``` + +Fix all remaining references. + +**Step 7:** Verify it compiles: + +```bash +go build ./pkg/sdk/client/... +``` + +**Step 8:** Run tests: + +```bash +go test ./pkg/sdk/client/... -count=1 +``` + +**Step 9:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container SDK client to docker" +``` + +--- + +## Chunk 8: Orchestrator DSL Helpers + +### Task 9: Add Orchestrator Docker Methods + +**Files:** + +- Create: `pkg/sdk/orchestrator/docker.go` +- Create: `pkg/sdk/orchestrator/docker_public_test.go` + +**Step 1:** Write the test file `docker_public_test.go` with a test suite +covering each helper method. Use table-driven tests. Mock the client responses +or test that the correct TaskFunc is created: + +```go +func (s *DockerPublicTestSuite) TestDockerPull() { + tests := []struct { + name string + target string + image string + validateFunc func(task *orchestrator.Task) + }{ + { + name: "creates task with correct name", + target: "_any", + image: "ubuntu:24.04", + validateFunc: func(task *orchestrator.Task) { + s.Equal("pull-image", task.Name()) + }, + }, + } + // ... +} +``` + +Test each method: `DockerPull`, `DockerCreate`, `DockerExec`, `DockerInspect`, +`DockerStart`, `DockerStop`, `DockerRemove`, `DockerList`. + +**Step 2:** Run tests to verify they fail: + +```bash +go test ./pkg/sdk/orchestrator/... -count=1 -run TestDocker +``` + +**Step 3:** Implement `docker.go` with methods on `*Plan`: + +```go +// DockerPull creates a task that pulls a Docker image on the target host. +func (p *Plan) DockerPull( + name string, + target string, + image string, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Pull(ctx, target, gen.DockerPullRequest{ + Image: image, + }) + if err != nil { + return nil, err + } + r := resp.Data.Results[0] + return &Result{ + Changed: true, + Data: map[string]any{ + "image_id": r.ImageID, + "tag": r.Tag, + "size": r.Size, + }, + }, nil + }) +} +``` + +Follow the same pattern for all 8 operations. Each method: + +- Takes the task name, target, and operation-specific params +- Returns `*Task` for chaining +- Wraps the SDK client call in a `TaskFunc` +- Sets `Changed: true` for mutations, `Changed: false` for reads (inspect, list) +- Populates `Data` with relevant result fields + +Methods to implement: + +- `DockerPull(name, target, image string) *Task` +- `DockerCreate(name, target string, body gen.DockerCreateRequest) *Task` +- `DockerStart(name, target, id string) *Task` +- `DockerStop(name, target, id string, body gen.DockerStopRequest) *Task` +- `DockerRemove(name, target, id string, params *gen.DeleteNodeContainerDockerByIDParams) *Task` +- `DockerExec(name, target, id string, body gen.DockerExecRequest) *Task` +- `DockerInspect(name, target, id string) *Task` +- `DockerList(name, target string, params *gen.GetNodeContainerDockerParams) *Task` + +**Step 4:** Run tests to verify they pass: + +```bash +go test ./pkg/sdk/orchestrator/... -count=1 +``` + +**Step 5:** Commit: + +```bash +git add -A && git commit -m "feat: add orchestrator Docker DSL helpers" +``` + +--- + +### Task 10: Rewrite Container Targeting Example + +**Files:** + +- Modify: `examples/sdk/orchestrator/features/container-targeting.go` + +**Step 1:** Rewrite the example to use the new DSL helpers: + +```go +plan := orchestrator.NewPlan(apiClient, + orchestrator.WithHooks(hooks), + orchestrator.OnError(orchestrator.Continue), +) + +pull := plan.DockerPull("pull-image", target, containerImage) + +create := plan.DockerCreate("create-container", target, + gen.DockerCreateRequest{ + Image: containerImage, + Name: ptr(containerName), + AutoStart: &autoStart, + Command: &[]string{"sleep", "600"}, + }, +) +create.DependsOn(pull) + +plan.DockerExec("exec-hostname", target, containerName, + gen.DockerExecRequest{Command: []string{"hostname"}}, +).DependsOn(create) + +plan.DockerInspect("inspect", target, containerName).DependsOn(create) + +plan.DockerRemove("cleanup", target, containerName, + &gen.DeleteNodeContainerDockerByIDParams{Force: &force}, +).DependsOn(create) +``` + +**Step 2:** Update SDK client example too: `examples/sdk/client/container.go` — +update to use `c.Docker.*` and `gen.Docker*` types. + +**Step 3:** Verify examples compile: + +```bash +go build ./examples/... +``` + +**Step 4:** Commit: + +```bash +git add -A && git commit -m "refactor: update examples to use docker DSL" +``` + +--- + +## Chunk 9: Integration Tests + +### Task 11: Rename Integration Tests + +**Files:** + +- Rename: `test/integration/container_test.go` → + `test/integration/docker_test.go` + +**Step 1:** Rename the file: + +```bash +git mv test/integration/container_test.go test/integration/docker_test.go +``` + +**Step 2:** Update the test to: + +- Use `c.Docker.*` instead of `c.Container.*` +- Use `gen.Docker*` types instead of `gen.Container*` +- Update CLI commands from `container list` to `container docker list` +- Rename suite/test names from `Container*` to `Docker*` + +**Step 3:** Verify it compiles: + +```bash +go build ./test/integration/... +``` + +**Step 4:** Commit: + +```bash +git add -A && git commit -m "refactor: rename container integration tests to docker" +``` + +--- + +## Chunk 10: Documentation + +### Task 12: Update Documentation + +**Files:** + +- Modify: `docs/docs/sidebar/features/container-management.md` — update to + describe Docker as first runtime, update all paths/permissions/CLI examples +- Rename: CLI docs from `docs/.../container/` to restructure under + `docs/.../container/docker/` +- Modify: SDK orchestrator docs to reference Docker methods +- Modify: `docs/docs/sidebar/usage/configuration.md` — update permission tables +- Modify: `docs/docs/sidebar/architecture/system-architecture.md` — update + endpoint tables +- Modify: `docs/docusaurus.config.ts` — update navbar links +- Modify: `CLAUDE.md` — update permission tables in role descriptions + +**Step 1:** Update `container-management.md`: + +- Update title, description +- Update all path examples from `/container` to `/container/docker` +- Update permissions table from `container:*` to `docker:*` +- Update CLI examples from `client container list` to + `client container docker list` +- Update role descriptions + +**Step 2:** Restructure CLI docs: + +- Current: `docs/.../cli/client/container/container.mdx` (parent) +- New: `docs/.../cli/client/container/container.mdx` stays as parent grouping +- Create: `docs/.../cli/client/container/docker/` directory +- Move per-operation docs into the docker subdirectory +- Update all command examples + +**Step 3:** Update SDK orchestrator operation docs: + +- Rename `container-create.md` → `docker-create.md`, etc. +- Update code examples to use `plan.DockerCreate(...)` etc. + +**Step 4:** Update configuration.md permission tables. + +**Step 5:** Update system-architecture.md endpoint tables. + +**Step 6:** Regenerate API docs: + +```bash +just docs::generate-api +``` + +**Step 7:** Commit: + +```bash +git add -A && git commit -m "docs: update documentation for docker domain rename" +``` + +--- + +## Chunk 11: Verify + +### Task 13: Full Verification + +**Step 1:** Regenerate everything: + +```bash +just generate +``` + +**Step 2:** Build: + +```bash +go build ./... +``` + +**Step 3:** Run all unit tests: + +```bash +just go::unit +``` + +**Step 4:** Run lint: + +```bash +just go::vet +``` + +**Step 5:** Search for any remaining `container` references that should be +`docker` (excluding the `container` parent command which is intentional): + +```bash +grep -rn 'container:read\|container:write\|container:execute' \ + --include='*.go' . | grep -v '.gen.go' +grep -rn 'ContainerService\|ContainerResult\|ContainerCreateData' \ + --include='*.go' . | grep -v '.gen.go' +grep -rn 'OperationContainer' --include='*.go' . +grep -rn 'containerProvider' --include='*.go' . +grep -rn 'processContainer' --include='*.go' . +``` + +All should return empty. + +**Step 6:** Verify CLI works: + +```bash +go run main.go client container --help +go run main.go client container docker --help +``` + +**Step 7:** Commit any final fixes: + +```bash +git add -A && git commit -m "chore: final verification cleanup" +``` + +--- + +## Files Modified + +| Repo | File | Change | +| ----- | --------------------------------------------------------------- | -------------------------------------- | +| osapi | `internal/api/container/` → `internal/api/docker/` | Full directory rename + content update | +| osapi | `internal/api/handler_container.go` → `handler_docker.go` | Rename + update | +| osapi | `internal/api/handler.go` | Update registration | +| osapi | `internal/api/types.go` | Rename handler field | +| osapi | `internal/api/handler_public_test.go` | Rename test | +| osapi | `internal/provider/container/` → `internal/provider/docker/` | Full directory rename | +| osapi | `internal/provider/container/runtime/driver.go` | DELETE | +| osapi | `internal/agent/processor_container.go` → `processor_docker.go` | Rename + update | +| osapi | `internal/agent/types.go` | Rename field | +| osapi | `internal/agent/agent.go` | Rename parameter | +| osapi | `internal/agent/factory.go` | Rename variable | +| osapi | `internal/agent/processor.go` | Update case | +| osapi | `internal/job/types.go` | Rename 8 constants + 6 types | +| osapi | `internal/job/client/modify_container.go` → `modify_docker.go` | Rename + update | +| osapi | `internal/authtoken/permissions.go` | Rename 3 constants | +| osapi | `cmd/client_container.go` | Simplify to parent | +| osapi | `cmd/client_container_docker.go` | NEW parent for docker | +| osapi | `cmd/client_container_*.go` → `client_container_docker_*.go` | Rename 8 files | +| osapi | `cmd/api_helpers.go` | Update handler call | +| osapi | `pkg/sdk/client/container.go` → `docker.go` | Rename + update | +| osapi | `pkg/sdk/client/container_types.go` → `docker_types.go` | Rename + update | +| osapi | `pkg/sdk/client/osapi.go` | Rename field | +| osapi | `pkg/sdk/orchestrator/docker.go` | NEW — DSL helpers | +| osapi | `pkg/sdk/orchestrator/docker_public_test.go` | NEW — tests | +| osapi | `examples/sdk/orchestrator/features/container-targeting.go` | Rewrite with DSL | +| osapi | `examples/sdk/client/container.go` | Update SDK calls | +| osapi | `test/integration/container_test.go` → `docker_test.go` | Rename + update | +| osapi | `docs/` (multiple) | Update paths, permissions, examples | diff --git a/examples/sdk/client/container.go b/examples/sdk/client/container.go index 67091f18..dbaa2cb8 100644 --- a/examples/sdk/client/container.go +++ b/examples/sdk/client/container.go @@ -50,7 +50,7 @@ func main() { target := "_any" // Pull an image. - pull, err := c.Container.Pull(ctx, target, gen.ContainerPullRequest{ + pull, err := c.Docker.Pull(ctx, target, gen.DockerPullRequest{ Image: "nginx:alpine", }) if err != nil { @@ -65,7 +65,7 @@ func main() { // Create a container. name := "osapi-example" autoStart := true - create, err := c.Container.Create(ctx, target, gen.ContainerCreateRequest{ + create, err := c.Docker.Create(ctx, target, gen.DockerCreateRequest{ Image: "nginx:alpine", Name: &name, AutoStart: &autoStart, @@ -83,7 +83,7 @@ func main() { // List running containers. state := gen.Running - list, err := c.Container.List(ctx, target, &gen.GetNodeContainerParams{ + list, err := c.Docker.List(ctx, target, &gen.GetNodeContainerDockerParams{ State: &state, }) if err != nil { @@ -98,7 +98,7 @@ func main() { } // Inspect the container. - inspect, err := c.Container.Inspect(ctx, target, containerID) + inspect, err := c.Docker.Inspect(ctx, target, containerID) if err != nil { log.Fatalf("inspect: %v", err) } @@ -109,7 +109,7 @@ func main() { } // Exec a command inside the container. - exec, err := c.Container.Exec(ctx, target, containerID, gen.ContainerExecRequest{ + exec, err := c.Docker.Exec(ctx, target, containerID, gen.DockerExecRequest{ Command: []string{"cat", "/etc/hostname"}, }) if err != nil { @@ -123,7 +123,7 @@ func main() { // Stop the container. timeout := 5 - stop, err := c.Container.Stop(ctx, target, containerID, gen.ContainerStopRequest{ + stop, err := c.Docker.Stop(ctx, target, containerID, gen.DockerStopRequest{ Timeout: &timeout, }) if err != nil { @@ -137,9 +137,14 @@ func main() { // Remove the container. force := true - remove, err := c.Container.Remove(ctx, target, containerID, &gen.DeleteNodeContainerByIdParams{ - Force: &force, - }) + remove, err := c.Docker.Remove( + ctx, + target, + containerID, + &gen.DeleteNodeContainerDockerByIDParams{ + Force: &force, + }, + ) if err != nil { log.Fatalf("remove: %v", err) } diff --git a/examples/sdk/orchestrator/features/container-targeting.go b/examples/sdk/orchestrator/features/container-targeting.go index 1e809c62..745b1f7d 100644 --- a/examples/sdk/orchestrator/features/container-targeting.go +++ b/examples/sdk/orchestrator/features/container-targeting.go @@ -18,24 +18,19 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// Package main demonstrates running provider operations inside Docker -// containers using the orchestrator SDK's typed ContainerProvider. -// -// The example exercises every read-only provider, command execution, -// error handling for failing commands, and a deliberately failing task -// to show how the orchestrator reports each status. +// Package main demonstrates orchestrating Docker container lifecycle +// operations using the DSL helpers. The plan composes pull, create, +// exec, inspect, and cleanup as a DAG. // // Expected output statuses: // -// changed — setup tasks (ensure-clean, pull, create, deploy) and command exec -// unchanged — all read-only providers (host, mem, load, disk) -// failed — deliberately-fails task (returns an error) -// skipped — none (OnError(Continue) lets independent tasks proceed) +// changed — pull, create, exec, cleanup +// unchanged — inspect (read-only) +// failed — deliberately-fails (returns an error) // // Prerequisites: // - A running OSAPI stack (API server + agent + NATS) // - Docker available on the agent host -// - Go toolchain (the example builds osapi for linux automatically) // // Run with: OSAPI_TOKEN="" go run container-targeting.go package main @@ -45,9 +40,6 @@ import ( "fmt" "log" "os" - osexec "os/exec" - "runtime" - "strings" "time" "github.com/retr0h/osapi/pkg/sdk/client" @@ -56,7 +48,7 @@ import ( ) const ( - containerName = "example-provider-container" + containerName = "example-orchestrator-container" containerImage = "ubuntu:24.04" ) @@ -97,401 +89,73 @@ func main() { }, } - // WithDockerExecFn bridges the orchestrator's Docker DSL to the - // OSAPI Container.Exec REST API. Every ExecProvider call flows - // through: SDK → REST API → NATS → agent → docker exec. - dockerExecFn := func( - ctx context.Context, - containerID string, - command []string, - ) (stdout, stderr string, exitCode int, err error) { - resp, execErr := apiClient.Container.Exec( - ctx, - target, - containerID, - gen.ContainerExecRequest{Command: command}, - ) - if execErr != nil { - return "", "", -1, execErr - } - - r := resp.Data.Results[0] - - return r.Stdout, r.Stderr, r.ExitCode, nil - } - plan := orchestrator.NewPlan(apiClient, orchestrator.WithHooks(hooks), - orchestrator.WithDockerExecFn(dockerExecFn), orchestrator.OnError(orchestrator.Continue), ) - // ── Setup: pull + create + deploy osapi ────────────────────── - - pull := plan.TaskFunc( - "pull-image", - func( - ctx context.Context, - c *client.Client, - ) (*orchestrator.Result, error) { - resp, err := c.Container.Pull(ctx, target, gen.ContainerPullRequest{ - Image: containerImage, - }) - if err != nil { - return nil, fmt.Errorf("pull: %w", err) - } - - r := resp.Data.Results[0] - - return &orchestrator.Result{ - Changed: true, - Data: map[string]any{"image_id": r.ImageID}, - }, nil - }, - ) + // ── Pre-cleanup: remove leftover container from previous run ─ + // Swallow errors — the container may not exist. - autoStart := true - create := plan.TaskFunc( - "create-container", + force := true + preCleanup := plan.TaskFunc("pre-cleanup", func( ctx context.Context, c *client.Client, ) (*orchestrator.Result, error) { - resp, err := c.Container.Create(ctx, target, gen.ContainerCreateRequest{ - Image: containerImage, - Name: ptr(containerName), - AutoStart: &autoStart, - Command: &[]string{"sleep", "600"}, - }) - if err != nil { - // Container already exists — carry on. - return &orchestrator.Result{}, nil - } - - r := resp.Data.Results[0] - - return &orchestrator.Result{ - Changed: true, - Data: map[string]any{"id": r.ID, "name": r.Name}, - }, nil - }, - ) - create.DependsOn(pull) - - // Build osapi for linux and docker cp it into the container. - // In production you would bake the binary into the image. - deploy := plan.TaskFunc( - "deploy-osapi", - func( - _ context.Context, - _ *client.Client, - ) (*orchestrator.Result, error) { - tmpBin := "/tmp/osapi-container" - - build := osexec.Command( - "go", "build", "-o", tmpBin, "github.com/retr0h/osapi", + _, _ = c.Docker.Remove(ctx, target, containerName, + &gen.DeleteNodeContainerDockerByIDParams{Force: &force}, ) - build.Dir = findProjectRoot() - build.Env = append(os.Environ(), "GOOS=linux", "GOARCH="+runtime.GOARCH) - if out, err := build.CombinedOutput(); err != nil { - return nil, fmt.Errorf("build osapi: %s: %w", string(out), err) - } - - cp := osexec.Command("docker", "cp", tmpBin, containerName+":/osapi") - if out, err := cp.CombinedOutput(); err != nil { - return nil, fmt.Errorf("docker cp: %s: %w", string(out), err) - } - - _ = os.Remove(tmpBin) - - return &orchestrator.Result{Changed: true}, nil - }, - ) - deploy.DependsOn(create) - - // ── Provider: typed ContainerProvider bound to the target ────── - - dockerTarget := plan.Docker(containerName, containerImage) - provider := orchestrator.NewContainerProvider(dockerTarget) - scoped := plan.In(dockerTarget) - - // ── Host provider: 9 read-only operations (unchanged) ───────── - - getHostname := scoped.TaskFunc( - "host/get-hostname", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetHostname(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" hostname = %s\n", v) - - return &orchestrator.Result{Data: map[string]any{"hostname": v}}, nil - }, - ) - getHostname.DependsOn(deploy) - - getOSInfo := scoped.TaskFunc( - "host/get-os-info", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - info, err := provider.GetOSInfo(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" os = %s %s\n", info.Distribution, info.Version) - - return &orchestrator.Result{ - Changed: info.Changed, - Data: map[string]any{ - "distribution": info.Distribution, - "version": info.Version, - }, - }, nil - }, - ) - getOSInfo.DependsOn(deploy) - - getArch := scoped.TaskFunc( - "host/get-architecture", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetArchitecture(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" architecture = %s\n", v) - - return &orchestrator.Result{Data: map[string]any{"architecture": v}}, nil - }, - ) - getArch.DependsOn(deploy) - - getKernel := scoped.TaskFunc( - "host/get-kernel-version", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetKernelVersion(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" kernel = %s\n", v) - - return &orchestrator.Result{Data: map[string]any{"kernel": v}}, nil - }, - ) - getKernel.DependsOn(deploy) - - getUptime := scoped.TaskFunc( - "host/get-uptime", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetUptime(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" uptime = %s\n", v) - return &orchestrator.Result{Data: map[string]any{"uptime": v.String()}}, nil + return &orchestrator.Result{Changed: false}, nil }, ) - getUptime.DependsOn(deploy) - - getFQDN := scoped.TaskFunc( - "host/get-fqdn", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetFQDN(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" fqdn = %s\n", v) - return &orchestrator.Result{Data: map[string]any{"fqdn": v}}, nil - }, - ) - getFQDN.DependsOn(deploy) - - getCPUCount := scoped.TaskFunc( - "host/get-cpu-count", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetCPUCount(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" cpu_count = %d\n", v) + // ── Pull image ─────────────────────────────────────────────── - return &orchestrator.Result{Data: map[string]any{"cpu_count": v}}, nil - }, - ) - getCPUCount.DependsOn(deploy) - - getSvcMgr := scoped.TaskFunc( - "host/get-service-manager", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetServiceManager(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" service_mgr = %s\n", v) + pull := plan.DockerPull("pull-image", target, containerImage) + pull.DependsOn(preCleanup) - return &orchestrator.Result{Data: map[string]any{"service_manager": v}}, nil - }, - ) - getSvcMgr.DependsOn(deploy) - - getPkgMgr := scoped.TaskFunc( - "host/get-package-manager", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - v, err := provider.GetPackageManager(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" package_mgr = %s\n", v) + // ── Create container ───────────────────────────────────────── - return &orchestrator.Result{Data: map[string]any{"package_manager": v}}, nil + autoStart := true + create := plan.DockerCreate("create-container", target, + gen.DockerCreateRequest{ + Image: containerImage, + Name: ptr(containerName), + AutoStart: &autoStart, + Command: &[]string{"sleep", "600"}, }, ) - getPkgMgr.DependsOn(deploy) + create.DependsOn(pull) - // ── Memory, load, disk providers (unchanged) ────────────────── + // ── Exec: run commands inside the container ────────────────── - getMemStats := scoped.TaskFunc( - "mem/get-stats", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - stats, err := provider.GetMemStats(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" mem_total = %d MB\n", stats.Total/1024/1024) - fmt.Printf(" mem_available = %d MB\n", stats.Available/1024/1024) - - return &orchestrator.Result{ - Changed: stats.Changed, - Data: map[string]any{ - "total": stats.Total, - "available": stats.Available, - }, - }, nil - }, + execHostname := plan.DockerExec("exec-hostname", target, containerName, + gen.DockerExecRequest{Command: []string{"hostname"}}, ) - getMemStats.DependsOn(deploy) - - getLoadStats := scoped.TaskFunc( - "load/get-average-stats", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - stats, err := provider.GetLoadStats(ctx) - if err != nil { - return nil, err - } - fmt.Printf(" load1 = %.2f\n", stats.Load1) - fmt.Printf(" load5 = %.2f\n", stats.Load5) - fmt.Printf(" load15 = %.2f\n", stats.Load15) - - return &orchestrator.Result{ - Changed: stats.Changed, - Data: map[string]any{ - "load1": stats.Load1, "load5": stats.Load5, "load15": stats.Load15, - }, - }, nil - }, - ) - getLoadStats.DependsOn(deploy) - - getDiskUsage := scoped.TaskFunc( - "disk/get-usage", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - disks, err := provider.GetDiskUsage(ctx) - if err != nil { - return nil, err - } - for _, d := range disks { - fmt.Printf(" disk %-8s total=%d MB used=%d MB\n", - d.Name, d.Total/1024/1024, d.Used/1024/1024) - } + execHostname.DependsOn(create) - return &orchestrator.Result{Data: map[string]any{"mounts": len(disks)}}, nil - }, + execUname := plan.DockerExec("exec-uname", target, containerName, + gen.DockerExecRequest{Command: []string{"uname", "-a"}}, ) - getDiskUsage.DependsOn(deploy) - - // ── Command provider: exec + shell (changed) ────────────────── - - execUname := scoped.TaskFunc( - "command/exec-uname", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - r, err := provider.Exec(ctx, orchestrator.ExecParams{ - Command: "uname", - Args: []string{"-a"}, - }) - if err != nil { - return nil, err - } - fmt.Printf(" uname -a = %s", r.Stdout) + execUname.DependsOn(create) - return &orchestrator.Result{ - Changed: r.Changed, - Data: map[string]any{"exit_code": r.ExitCode}, - }, nil + execOS := plan.DockerExec("exec-os-release", target, containerName, + gen.DockerExecRequest{ + Command: []string{"sh", "-c", "head -2 /etc/os-release"}, }, ) - execUname.DependsOn(deploy) - - // Reads /etc/os-release inside the container to prove we are - // running inside Ubuntu 24.04, not the host OS. - shellOSRelease := scoped.TaskFunc( - "command/shell-os-release", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - r, err := provider.Shell(ctx, orchestrator.ShellParams{ - Command: "head -2 /etc/os-release && echo container-hostname=$(hostname)", - }) - if err != nil { - return nil, err - } - fmt.Printf(" os-release =\n%s", r.Stdout) + execOS.DependsOn(create) - return &orchestrator.Result{ - Changed: r.Changed, - Data: map[string]any{"exit_code": r.ExitCode}, - }, nil - }, - ) - shellOSRelease.DependsOn(deploy) + // ── Inspect: read-only, reports unchanged ──────────────────── - // ── Command that exits non-zero: handled gracefully ─────────── - // - // The command provider returns the exit code in the result. - // The task inspects it and reports unchanged (no system mutation) - // rather than failing the task. - - execFails := scoped.TaskFunc( - "command/exec-nonzero", - func(ctx context.Context, _ *client.Client) (*orchestrator.Result, error) { - r, err := provider.Exec(ctx, orchestrator.ExecParams{ - Command: "ls", - Args: []string{"/does-not-exist"}, - }) - if err != nil { - return nil, err - } + inspect := plan.DockerInspect("inspect-container", target, containerName) + inspect.DependsOn(create) - fmt.Printf(" exit_code = %d\n", r.ExitCode) - fmt.Printf(" stderr = %s", r.Stderr) - - return &orchestrator.Result{ - Changed: r.Changed, - Data: map[string]any{ - "exit_code": r.ExitCode, - "stderr": r.Stderr, - }, - }, nil - }, - ) - execFails.DependsOn(deploy) - - // ── Deliberately failing task: shows StatusFailed ────────────── - // - // Returning an error from the task function marks it as failed. - // With OnError(Continue), independent tasks keep running but - // any task that DependsOn this one would be skipped. + // ── Deliberately failing task: shows StatusFailed ───────────── - deliberatelyFails := scoped.TaskFunc( - "deliberately-fails", + deliberatelyFails := plan.TaskFunc("deliberately-fails", func( _ context.Context, _ *client.Client, @@ -499,47 +163,18 @@ func main() { return nil, fmt.Errorf("this task always fails to demonstrate error reporting") }, ) - deliberatelyFails.DependsOn(deploy) + deliberatelyFails.DependsOn(create) - // ── Cleanup ─────────────────────────────────────────────────── - // - // Depends on all provider tasks EXCEPT deliberately-fails so - // that cleanup is not skipped when OnError(Continue) is active. + // ── Cleanup ────────────────────────────────────────────────── + // Depends on all tasks that use the container so it runs last. - cleanup := plan.TaskFunc( - "cleanup", - func( - ctx context.Context, - c *client.Client, - ) (*orchestrator.Result, error) { - force := true - _, err := c.Container.Remove( - ctx, - target, - containerName, - &gen.DeleteNodeContainerByIDParams{Force: &force}, - ) - if err != nil { - return nil, fmt.Errorf("remove: %w", err) - } - - return &orchestrator.Result{Changed: true}, nil - }, - ) - cleanup.DependsOn( - getHostname, getOSInfo, getArch, getKernel, getUptime, - getFQDN, getCPUCount, getSvcMgr, getPkgMgr, - getMemStats, getLoadStats, getDiskUsage, - execUname, shellOSRelease, execFails, - ) - - // Suppress unused variable warning — deliberately-fails has no - // dependents by design. - _ = deliberatelyFails + plan.DockerRemove("cleanup", target, containerName, + &gen.DeleteNodeContainerDockerByIDParams{Force: &force}, + ).DependsOn(execHostname, execUname, execOS, inspect) - // ── Run ─────────────────────────────────────────────────────── + // ── Run ────────────────────────────────────────────────────── - fmt.Println("=== Container Provider Example ===") + fmt.Println("=== Docker Orchestration Example ===") fmt.Println() report, err := plan.Run(context.Background()) @@ -553,21 +188,3 @@ func main() { report.Duration.Truncate(time.Millisecond), ) } - -// findProjectRoot walks up from the current directory to find go.mod. -func findProjectRoot() string { - dir, _ := os.Getwd() - - for { - if _, err := os.Stat(dir + "/go.mod"); err == nil { - return dir - } - - idx := strings.LastIndex(dir, "/") - if idx <= 0 { - return "." - } - - dir = dir[:idx] - } -} diff --git a/internal/agent/agent.go b/internal/agent/agent.go index cec8d9e8..28594c96 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -30,7 +30,7 @@ import ( "github.com/retr0h/osapi/internal/job/client" "github.com/retr0h/osapi/internal/provider" "github.com/retr0h/osapi/internal/provider/command" - containerProv "github.com/retr0h/osapi/internal/provider/container" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" fileProv "github.com/retr0h/osapi/internal/provider/file" "github.com/retr0h/osapi/internal/provider/network/dns" "github.com/retr0h/osapi/internal/provider/network/netinfo" @@ -57,28 +57,28 @@ func New( netinfoProvider netinfo.Provider, commandProvider command.Provider, fileProvider fileProv.Provider, - containerProvider containerProv.Provider, + dockerProvider dockerProv.Provider, registryKV jetstream.KeyValue, factsKV jetstream.KeyValue, ) *Agent { a := &Agent{ - logger: logger, - appConfig: appConfig, - appFs: appFs, - jobClient: jobClient, - streamName: streamName, - hostProvider: hostProvider, - diskProvider: diskProvider, - memProvider: memProvider, - loadProvider: loadProvider, - dnsProvider: dnsProvider, - pingProvider: pingProvider, - netinfoProvider: netinfoProvider, - commandProvider: commandProvider, - fileProvider: fileProvider, - containerProvider: containerProvider, - registryKV: registryKV, - factsKV: factsKV, + logger: logger, + appConfig: appConfig, + appFs: appFs, + jobClient: jobClient, + streamName: streamName, + hostProvider: hostProvider, + diskProvider: diskProvider, + memProvider: memProvider, + loadProvider: loadProvider, + dnsProvider: dnsProvider, + pingProvider: pingProvider, + netinfoProvider: netinfoProvider, + commandProvider: commandProvider, + fileProvider: fileProvider, + dockerProvider: dockerProvider, + registryKV: registryKV, + factsKV: factsKV, } // Wire agent facts into all providers so they can access the latest @@ -94,7 +94,7 @@ func New( netinfoProvider, commandProvider, fileProvider, - containerProvider, + dockerProvider, ) return a diff --git a/internal/agent/factory.go b/internal/agent/factory.go index cf22c687..8c727e46 100644 --- a/internal/agent/factory.go +++ b/internal/agent/factory.go @@ -26,8 +26,7 @@ import ( "github.com/retr0h/osapi/internal/exec" "github.com/retr0h/osapi/internal/provider/command" - containerProv "github.com/retr0h/osapi/internal/provider/container" - "github.com/retr0h/osapi/internal/provider/container/runtime/docker" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" "github.com/retr0h/osapi/internal/provider/network/dns" "github.com/retr0h/osapi/internal/provider/network/netinfo" "github.com/retr0h/osapi/internal/provider/network/ping" @@ -38,8 +37,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/platform" ) -// factoryDockerNewFn is the function used to create a Docker driver (injectable for testing). -var factoryDockerNewFn = docker.New +// factoryDockerNewFn is the function used to create a Docker provider (injectable for testing). +var factoryDockerNewFn = dockerProv.New // ProviderFactory creates platform-specific providers for the agent. type ProviderFactory struct { @@ -65,7 +64,7 @@ func (f *ProviderFactory) CreateProviders() ( ping.Provider, netinfo.Provider, command.Provider, - containerProv.Provider, + dockerProv.Provider, ) { plat := platform.Detect() @@ -148,20 +147,20 @@ func (f *ProviderFactory) CreateProviders() ( // Create command provider (cross-platform, uses exec.Manager) commandProvider := command.New(f.logger, execManager) - // Create container provider (conditional on Docker availability) - var containerProvider containerProv.Provider - dockerDriver, err := factoryDockerNewFn() + // Create Docker provider (conditional on Docker availability) + var dockerProvider dockerProv.Provider + dockerClient, err := factoryDockerNewFn() if err == nil { - if pingErr := dockerDriver.Ping(context.Background()); pingErr == nil { - containerProvider = containerProv.New(dockerDriver) + if pingErr := dockerClient.Ping(context.Background()); pingErr == nil { + dockerProvider = dockerClient } else { - f.logger.Info("Docker not available, container operations disabled", + f.logger.Info("Docker not available, docker operations disabled", slog.String("error", pingErr.Error())) } } else { - f.logger.Info("Docker client creation failed, container operations disabled", + f.logger.Info("Docker client creation failed, docker operations disabled", slog.String("error", err.Error())) } - return hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, containerProvider + return hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, dockerProvider } diff --git a/internal/agent/factory_public_test.go b/internal/agent/factory_public_test.go index 5ea260ad..a27ae41a 100644 --- a/internal/agent/factory_public_test.go +++ b/internal/agent/factory_public_test.go @@ -74,7 +74,7 @@ func (s *FactoryPublicTestSuite) TestCreateProviders() { s.NotNil(pingProvider) s.NotNil(netinfoProvider) s.NotNil(commandProvider) - // containerProvider can be nil if Docker is not available + // dockerProvider can be nil if Docker is not available }) } } diff --git a/internal/agent/factory_test.go b/internal/agent/factory_test.go index 6655d154..52c62cec 100644 --- a/internal/agent/factory_test.go +++ b/internal/agent/factory_test.go @@ -32,7 +32,7 @@ import ( "github.com/shirou/gopsutil/v4/host" "github.com/stretchr/testify/suite" - "github.com/retr0h/osapi/internal/provider/container/runtime/docker" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" "github.com/retr0h/osapi/pkg/sdk/platform" ) @@ -102,7 +102,7 @@ func (s *FactoryTestSuite) TestCreateProviders() { } }, setupDocker: func() { - factoryDockerNewFn = func() (*docker.Driver, error) { + factoryDockerNewFn = func() (*dockerProv.Client, error) { return nil, fmt.Errorf("docker not installed") } }, @@ -116,8 +116,8 @@ func (s *FactoryTestSuite) TestCreateProviders() { } }, setupDocker: func() { - factoryDockerNewFn = func() (*docker.Driver, error) { - return docker.NewWithClient(&testDockerClient{ + factoryDockerNewFn = func() (*dockerProv.Client, error) { + return dockerProv.NewWithClient(&testDockerClient{ pingErr: errors.New("connection refused"), }), nil } @@ -132,8 +132,8 @@ func (s *FactoryTestSuite) TestCreateProviders() { } }, setupDocker: func() { - factoryDockerNewFn = func() (*docker.Driver, error) { - return docker.NewWithClient(&testDockerClient{}), nil + factoryDockerNewFn = func() (*dockerProv.Client, error) { + return dockerProv.NewWithClient(&testDockerClient{}), nil } }, wantContainer: true, @@ -155,7 +155,7 @@ func (s *FactoryTestSuite) TestCreateProviders() { } factory := NewProviderFactory(slog.Default()) - hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, containerProvider := factory.CreateProviders() + hostProvider, diskProvider, memProvider, loadProvider, dnsProvider, pingProvider, netinfoProvider, commandProvider, dockerProvider := factory.CreateProviders() s.NotNil(hostProvider) s.NotNil(diskProvider) @@ -167,7 +167,7 @@ func (s *FactoryTestSuite) TestCreateProviders() { s.NotNil(commandProvider) if tt.wantContainer { - s.NotNil(containerProvider) + s.NotNil(dockerProvider) } }) } diff --git a/internal/agent/processor.go b/internal/agent/processor.go index 62a041da..73af45c8 100644 --- a/internal/agent/processor.go +++ b/internal/agent/processor.go @@ -54,8 +54,8 @@ func (a *Agent) processJobOperation( return a.processCommandOperation(jobRequest) case "file": return a.processFileOperation(jobRequest) - case "container": - return a.processContainerOperation(jobRequest) + case "docker": + return a.processDockerOperation(jobRequest) default: return nil, fmt.Errorf("unsupported job category: %s", jobRequest.Category) } diff --git a/internal/agent/processor_container.go b/internal/agent/processor_docker.go similarity index 65% rename from internal/agent/processor_container.go rename to internal/agent/processor_docker.go index 5fe8c7da..31d9e7ad 100644 --- a/internal/agent/processor_container.go +++ b/internal/agent/processor_docker.go @@ -28,15 +28,15 @@ import ( "time" "github.com/retr0h/osapi/internal/job" - "github.com/retr0h/osapi/internal/provider/container/runtime" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" ) -// processContainerOperation handles container-related operations. -func (a *Agent) processContainerOperation( +// processDockerOperation handles docker-related operations. +func (a *Agent) processDockerOperation( jobRequest job.Request, ) (json.RawMessage, error) { - if a.containerProvider == nil { - return nil, fmt.Errorf("container runtime not available") + if a.dockerProvider == nil { + return nil, fmt.Errorf("docker runtime not available") } ctx := context.Background() @@ -46,48 +46,48 @@ func (a *Agent) processContainerOperation( switch baseOperation { case "create": - return a.processContainerCreate(ctx, jobRequest) + return a.processDockerCreate(ctx, jobRequest) case "start": - return a.processContainerStart(ctx, jobRequest) + return a.processDockerStart(ctx, jobRequest) case "stop": - return a.processContainerStop(ctx, jobRequest) + return a.processDockerStop(ctx, jobRequest) case "remove": - return a.processContainerRemove(ctx, jobRequest) + return a.processDockerRemove(ctx, jobRequest) case "list": - return a.processContainerList(ctx, jobRequest) + return a.processDockerList(ctx, jobRequest) case "inspect": - return a.processContainerInspect(ctx, jobRequest) + return a.processDockerInspect(ctx, jobRequest) case "exec": - return a.processContainerExec(ctx, jobRequest) + return a.processDockerExec(ctx, jobRequest) case "pull": - return a.processContainerPull(ctx, jobRequest) + return a.processDockerPull(ctx, jobRequest) default: - return nil, fmt.Errorf("unsupported container operation: %s", jobRequest.Operation) + return nil, fmt.Errorf("unsupported docker operation: %s", jobRequest.Operation) } } -// processContainerCreate handles container creation. -func (a *Agent) processContainerCreate( +// processDockerCreate handles docker container creation. +func (a *Agent) processDockerCreate( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { - var data job.ContainerCreateData + var data job.DockerCreateData if err := json.Unmarshal(jobRequest.Data, &data); err != nil { return nil, fmt.Errorf("unmarshal create data: %w", err) } // Map ports and volumes from job types to runtime types - var ports []runtime.PortMapping + var ports []dockerProv.PortMapping for _, p := range data.Ports { - ports = append(ports, runtime.PortMapping{Host: p.Host, Container: p.Container}) + ports = append(ports, dockerProv.PortMapping{Host: p.Host, Container: p.Container}) } - var volumes []runtime.VolumeMapping + var volumes []dockerProv.VolumeMapping for _, v := range data.Volumes { - volumes = append(volumes, runtime.VolumeMapping{Host: v.Host, Container: v.Container}) + volumes = append(volumes, dockerProv.VolumeMapping{Host: v.Host, Container: v.Container}) } - result, err := a.containerProvider.Create(ctx, runtime.CreateParams{ + result, err := a.dockerProvider.Create(ctx, dockerProv.CreateParams{ Image: data.Image, Name: data.Name, Command: data.Command, @@ -103,8 +103,8 @@ func (a *Agent) processContainerCreate( return json.Marshal(result) } -// processContainerStart handles starting a container. -func (a *Agent) processContainerStart( +// processDockerStart handles starting a docker container. +func (a *Agent) processDockerStart( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -115,18 +115,16 @@ func (a *Agent) processContainerStart( return nil, fmt.Errorf("unmarshal start data: %w", err) } - if err := a.containerProvider.Start(ctx, data.ID); err != nil { + result, err := a.dockerProvider.Start(ctx, data.ID) + if err != nil { return nil, err } - result := map[string]interface{}{ - "message": "Container started successfully", - } return json.Marshal(result) } -// processContainerStop handles stopping a container. -func (a *Agent) processContainerStop( +// processDockerStop handles stopping a docker container. +func (a *Agent) processDockerStop( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -144,18 +142,16 @@ func (a *Agent) processContainerStop( timeout = &d } - if err := a.containerProvider.Stop(ctx, data.ID, timeout); err != nil { + result, err := a.dockerProvider.Stop(ctx, data.ID, timeout) + if err != nil { return nil, err } - result := map[string]interface{}{ - "message": "Container stopped successfully", - } return json.Marshal(result) } -// processContainerRemove handles removing a container. -func (a *Agent) processContainerRemove( +// processDockerRemove handles removing a docker container. +func (a *Agent) processDockerRemove( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -167,27 +163,25 @@ func (a *Agent) processContainerRemove( return nil, fmt.Errorf("unmarshal remove data: %w", err) } - if err := a.containerProvider.Remove(ctx, data.ID, data.Force); err != nil { + result, err := a.dockerProvider.Remove(ctx, data.ID, data.Force) + if err != nil { return nil, err } - result := map[string]interface{}{ - "message": "Container removed successfully", - } return json.Marshal(result) } -// processContainerList handles listing containers. -func (a *Agent) processContainerList( +// processDockerList handles listing docker containers. +func (a *Agent) processDockerList( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { - var data job.ContainerListData + var data job.DockerListData if err := json.Unmarshal(jobRequest.Data, &data); err != nil { return nil, fmt.Errorf("unmarshal list data: %w", err) } - result, err := a.containerProvider.List(ctx, runtime.ListParams{ + result, err := a.dockerProvider.List(ctx, dockerProv.ListParams{ State: data.State, Limit: data.Limit, }) @@ -198,8 +192,8 @@ func (a *Agent) processContainerList( return json.Marshal(result) } -// processContainerInspect handles inspecting a container. -func (a *Agent) processContainerInspect( +// processDockerInspect handles inspecting a docker container. +func (a *Agent) processDockerInspect( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -210,7 +204,7 @@ func (a *Agent) processContainerInspect( return nil, fmt.Errorf("unmarshal inspect data: %w", err) } - result, err := a.containerProvider.Inspect(ctx, data.ID) + result, err := a.dockerProvider.Inspect(ctx, data.ID) if err != nil { return nil, err } @@ -218,8 +212,8 @@ func (a *Agent) processContainerInspect( return json.Marshal(result) } -// processContainerExec handles executing a command in a container. -func (a *Agent) processContainerExec( +// processDockerExec handles executing a command in a docker container. +func (a *Agent) processDockerExec( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -233,7 +227,7 @@ func (a *Agent) processContainerExec( return nil, fmt.Errorf("unmarshal exec data: %w", err) } - result, err := a.containerProvider.Exec(ctx, data.ID, runtime.ExecParams{ + result, err := a.dockerProvider.Exec(ctx, data.ID, dockerProv.ExecParams{ Command: data.Command, Env: data.Env, WorkingDir: data.WorkingDir, @@ -245,8 +239,8 @@ func (a *Agent) processContainerExec( return json.Marshal(result) } -// processContainerPull handles pulling a container image. -func (a *Agent) processContainerPull( +// processDockerPull handles pulling a docker image. +func (a *Agent) processDockerPull( ctx context.Context, jobRequest job.Request, ) (json.RawMessage, error) { @@ -257,7 +251,7 @@ func (a *Agent) processContainerPull( return nil, fmt.Errorf("unmarshal pull data: %w", err) } - result, err := a.containerProvider.Pull(ctx, data.Image) + result, err := a.dockerProvider.Pull(ctx, data.Image) if err != nil { return nil, err } diff --git a/internal/agent/processor_container_test.go b/internal/agent/processor_docker_test.go similarity index 72% rename from internal/agent/processor_container_test.go rename to internal/agent/processor_docker_test.go index 33083b5f..86922195 100644 --- a/internal/agent/processor_container_test.go +++ b/internal/agent/processor_docker_test.go @@ -35,9 +35,8 @@ import ( "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/job/mocks" commandMocks "github.com/retr0h/osapi/internal/provider/command/mocks" - containerProv "github.com/retr0h/osapi/internal/provider/container" - containerMocks "github.com/retr0h/osapi/internal/provider/container/mocks" - "github.com/retr0h/osapi/internal/provider/container/runtime" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" + dockerMocks "github.com/retr0h/osapi/internal/provider/docker/mocks" fileMocks "github.com/retr0h/osapi/internal/provider/file/mocks" dnsMocks "github.com/retr0h/osapi/internal/provider/network/dns/mocks" netinfoMocks "github.com/retr0h/osapi/internal/provider/network/netinfo/mocks" @@ -48,24 +47,24 @@ import ( memMocks "github.com/retr0h/osapi/internal/provider/node/mem/mocks" ) -type ProcessorContainerTestSuite struct { +type ProcessorDockerTestSuite struct { suite.Suite mockCtrl *gomock.Controller mockJobClient *mocks.MockJobClient } -func (s *ProcessorContainerTestSuite) SetupTest() { +func (s *ProcessorDockerTestSuite) SetupTest() { s.mockCtrl = gomock.NewController(s.T()) s.mockJobClient = mocks.NewMockJobClient(s.mockCtrl) } -func (s *ProcessorContainerTestSuite) TearDownTest() { +func (s *ProcessorDockerTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ProcessorContainerTestSuite) newAgentWithContainerMock( - containerMock containerProv.Provider, +func (s *ProcessorDockerTestSuite) newAgentWithContainerMock( + containerMock dockerProv.Provider, ) *Agent { return New( afero.NewMemMapFs(), @@ -88,11 +87,11 @@ func (s *ProcessorContainerTestSuite) newAgentWithContainerMock( ) } -func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { +func (s *ProcessorDockerTestSuite) TestProcessDockerOperation() { tests := []struct { name string jobRequest job.Request - setupMock func(*containerMocks.MockProvider) + setupMock func(*dockerMocks.MockProvider) expectError bool errorMsg string validate func(json.RawMessage) @@ -101,103 +100,107 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "nil provider returns error", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage(`{"image":"nginx:latest"}`), }, setupMock: nil, expectError: true, - errorMsg: "container runtime not available", + errorMsg: "docker runtime not available", }, { name: "successful container create", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage( `{"image":"nginx:latest","name":"web","auto_start":true}`, ), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). - Create(gomock.Any(), runtime.CreateParams{ + Create(gomock.Any(), dockerProv.CreateParams{ Image: "nginx:latest", Name: "web", AutoStart: true, }). - Return(&runtime.Container{ - ID: "abc123", - Name: "web", - Image: "nginx:latest", - State: "created", + Return(&dockerProv.Container{ + ID: "abc123", + Name: "web", + Image: "nginx:latest", + State: "created", + Changed: true, }, nil) }, validate: func(result json.RawMessage) { - var r runtime.Container + var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) - s.Equal("abc123", r.ID) - s.Equal("web", r.Name) + s.Equal("abc123", r["id"]) + s.Equal("web", r["name"]) + s.Equal(true, r["changed"]) }, }, { name: "successful container create with ports and volumes", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage( `{"image":"nginx:latest","name":"web","ports":[{"host":8080,"container":80}],"volumes":[{"host":"/data","container":"/var/data"}]}`, ), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). - Create(gomock.Any(), runtime.CreateParams{ + Create(gomock.Any(), dockerProv.CreateParams{ Image: "nginx:latest", Name: "web", - Ports: []runtime.PortMapping{ + Ports: []dockerProv.PortMapping{ {Host: 8080, Container: 80}, }, - Volumes: []runtime.VolumeMapping{ + Volumes: []dockerProv.VolumeMapping{ {Host: "/data", Container: "/var/data"}, }, }). - Return(&runtime.Container{ - ID: "def456", - Name: "web", - Image: "nginx:latest", - State: "created", + Return(&dockerProv.Container{ + ID: "def456", + Name: "web", + Image: "nginx:latest", + State: "created", + Changed: true, }, nil) }, validate: func(result json.RawMessage) { - var r runtime.Container + var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) - s.Equal("def456", r.ID) + s.Equal("def456", r["id"]) + s.Equal(true, r["changed"]) }, }, { - name: "unsupported container operation", + name: "unsupported docker operation", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "unknown.get", Data: json.RawMessage(`{}`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, - errorMsg: "unsupported container operation", + errorMsg: "unsupported docker operation", }, { name: "create with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal create data", }, @@ -205,11 +208,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on create", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage(`{"image":"nginx:latest"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Create(gomock.Any(), gomock.Any()). Return(nil, errors.New("docker error")) @@ -222,31 +225,35 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container start", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "start.execute", Data: json.RawMessage(`{"id":"abc123"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Start(gomock.Any(), "abc123"). - Return(nil) + Return(&dockerProv.ActionResult{ + Message: "Container started successfully", + Changed: true, + }, nil) }, validate: func(result json.RawMessage) { var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) s.Contains(r, "message") + s.Equal(true, r["changed"]) }, }, { name: "start with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "start.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal start data", }, @@ -254,14 +261,14 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on start", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "start.execute", Data: json.RawMessage(`{"id":"abc123"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Start(gomock.Any(), "abc123"). - Return(errors.New("start failed")) + Return(nil, errors.New("start failed")) }, expectError: true, errorMsg: "start failed", @@ -271,51 +278,59 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container stop with timeout", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "stop.execute", Data: json.RawMessage(`{"id":"abc123","timeout":10}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Stop(gomock.Any(), "abc123", gomock.Any()). - Return(nil) + Return(&dockerProv.ActionResult{ + Message: "Container stopped successfully", + Changed: true, + }, nil) }, validate: func(result json.RawMessage) { var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) s.Contains(r, "message") + s.Equal(true, r["changed"]) }, }, { name: "successful container stop without timeout", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "stop.execute", Data: json.RawMessage(`{"id":"abc123"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Stop(gomock.Any(), "abc123", (*time.Duration)(nil)). - Return(nil) + Return(&dockerProv.ActionResult{ + Message: "Container stopped successfully", + Changed: true, + }, nil) }, validate: func(result json.RawMessage) { var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) s.Contains(r, "message") + s.Equal(true, r["changed"]) }, }, { name: "stop with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "stop.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal stop data", }, @@ -323,14 +338,14 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on stop", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "stop.execute", Data: json.RawMessage(`{"id":"abc123","timeout":10}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Stop(gomock.Any(), "abc123", gomock.Any()). - Return(errors.New("stop failed")) + Return(nil, errors.New("stop failed")) }, expectError: true, errorMsg: "stop failed", @@ -340,31 +355,35 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container remove", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "remove.execute", Data: json.RawMessage(`{"id":"abc123","force":true}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Remove(gomock.Any(), "abc123", true). - Return(nil) + Return(&dockerProv.ActionResult{ + Message: "Container removed successfully", + Changed: true, + }, nil) }, validate: func(result json.RawMessage) { var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) s.Contains(r, "message") + s.Equal(true, r["changed"]) }, }, { name: "remove with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "remove.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal remove data", }, @@ -372,14 +391,14 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on remove", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "remove.execute", Data: json.RawMessage(`{"id":"abc123","force":false}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Remove(gomock.Any(), "abc123", false). - Return(errors.New("remove failed")) + Return(nil, errors.New("remove failed")) }, expectError: true, errorMsg: "remove failed", @@ -389,22 +408,22 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container list", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "list.get", Data: json.RawMessage(`{"state":"running","limit":10}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). - List(gomock.Any(), runtime.ListParams{ + List(gomock.Any(), dockerProv.ListParams{ State: "running", Limit: 10, }). - Return([]runtime.Container{ + Return([]dockerProv.Container{ {ID: "abc123"}, }, nil) }, validate: func(result json.RawMessage) { - var r []runtime.Container + var r []dockerProv.Container err := json.Unmarshal(result, &r) s.NoError(err) s.Len(r, 1) @@ -415,11 +434,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "list with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "list.get", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal list data", }, @@ -427,11 +446,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on list", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "list.get", Data: json.RawMessage(`{"state":"all"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). List(gomock.Any(), gomock.Any()). Return(nil, errors.New("list failed")) @@ -444,19 +463,19 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container inspect", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "inspect.get", Data: json.RawMessage(`{"id":"abc123"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Inspect(gomock.Any(), "abc123"). - Return(&runtime.ContainerDetail{ - Container: runtime.Container{ID: "abc123"}, + Return(&dockerProv.ContainerDetail{ + Container: dockerProv.Container{ID: "abc123"}, }, nil) }, validate: func(result json.RawMessage) { - var r runtime.ContainerDetail + var r dockerProv.ContainerDetail err := json.Unmarshal(result, &r) s.NoError(err) s.Equal("abc123", r.ID) @@ -466,11 +485,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "inspect with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "inspect.get", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal inspect data", }, @@ -478,11 +497,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on inspect", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "inspect.get", Data: json.RawMessage(`{"id":"abc123"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Inspect(gomock.Any(), "abc123"). Return(nil, errors.New("inspect failed")) @@ -495,37 +514,39 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container exec", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "exec.execute", Data: json.RawMessage(`{"id":"abc123","command":["ls","-la"]}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). - Exec(gomock.Any(), "abc123", runtime.ExecParams{ + Exec(gomock.Any(), "abc123", dockerProv.ExecParams{ Command: []string{"ls", "-la"}, }). - Return(&runtime.ExecResult{ + Return(&dockerProv.ExecResult{ Stdout: "output", ExitCode: 0, + Changed: true, }, nil) }, validate: func(result json.RawMessage) { - var r runtime.ExecResult + var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) - s.Equal("output", r.Stdout) - s.Equal(0, r.ExitCode) + s.Equal("output", r["stdout"]) + s.Equal(float64(0), r["exit_code"]) + s.Equal(true, r["changed"]) }, }, { name: "exec with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "exec.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal exec data", }, @@ -533,11 +554,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on exec", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "exec.execute", Data: json.RawMessage(`{"id":"abc123","command":["ls"]}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Exec(gomock.Any(), "abc123", gomock.Any()). Return(nil, errors.New("exec failed")) @@ -550,33 +571,35 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "successful container pull", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "pull.execute", Data: json.RawMessage(`{"image":"nginx:latest"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Pull(gomock.Any(), "nginx:latest"). - Return(&runtime.PullResult{ + Return(&dockerProv.PullResult{ ImageID: "sha256:abc", + Changed: true, }, nil) }, validate: func(result json.RawMessage) { - var r runtime.PullResult + var r map[string]interface{} err := json.Unmarshal(result, &r) s.NoError(err) - s.Equal("sha256:abc", r.ImageID) + s.Equal("sha256:abc", r["image_id"]) + s.Equal(true, r["changed"]) }, }, { name: "pull with invalid JSON data", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "pull.execute", Data: json.RawMessage(`invalid json`), }, - setupMock: func(_ *containerMocks.MockProvider) {}, + setupMock: func(_ *dockerMocks.MockProvider) {}, expectError: true, errorMsg: "unmarshal pull data", }, @@ -584,11 +607,11 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { name: "provider error on pull", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "pull.execute", Data: json.RawMessage(`{"image":"nginx:latest"}`), }, - setupMock: func(m *containerMocks.MockProvider) { + setupMock: func(m *dockerMocks.MockProvider) { m.EXPECT(). Pull(gomock.Any(), "nginx:latest"). Return(nil, errors.New("pull failed")) @@ -602,7 +625,7 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { s.Run(tt.name, func() { var a *Agent if tt.setupMock != nil { - containerMock := containerMocks.NewMockProvider(s.mockCtrl) + containerMock := dockerMocks.NewMockProvider(s.mockCtrl) tt.setupMock(containerMock) a = s.newAgentWithContainerMock(containerMock) } else { @@ -610,7 +633,7 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { a = s.newAgentWithContainerMock(nil) } - result, err := a.processContainerOperation(tt.jobRequest) + result, err := a.processDockerOperation(tt.jobRequest) if tt.expectError { s.Error(err) @@ -627,6 +650,6 @@ func (s *ProcessorContainerTestSuite) TestProcessContainerOperation() { } } -func TestProcessorContainerTestSuite(t *testing.T) { - suite.Run(t, new(ProcessorContainerTestSuite)) +func TestProcessorDockerTestSuite(t *testing.T) { + suite.Run(t, new(ProcessorDockerTestSuite)) } diff --git a/internal/agent/processor_test.go b/internal/agent/processor_test.go index 57f61bb6..34b792cc 100644 --- a/internal/agent/processor_test.go +++ b/internal/agent/processor_test.go @@ -377,15 +377,15 @@ func (s *ProcessorTestSuite) TestProcessJobOperation() { }, }, { - name: "container category routes to container processor", + name: "docker category routes to docker processor", jobRequest: job.Request{ Type: job.TypeModify, - Category: "container", + Category: "docker", Operation: "create.execute", Data: json.RawMessage(`{"image":"nginx:latest"}`), }, expectError: true, - errorMsg: "container runtime not available", + errorMsg: "docker runtime not available", }, { name: "unsupported job category", diff --git a/internal/agent/types.go b/internal/agent/types.go index 894d39e7..a2d91457 100644 --- a/internal/agent/types.go +++ b/internal/agent/types.go @@ -33,7 +33,7 @@ import ( "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/job/client" "github.com/retr0h/osapi/internal/provider/command" - containerProv "github.com/retr0h/osapi/internal/provider/container" + dockerProv "github.com/retr0h/osapi/internal/provider/docker" fileProv "github.com/retr0h/osapi/internal/provider/file" "github.com/retr0h/osapi/internal/provider/network/dns" "github.com/retr0h/osapi/internal/provider/network/netinfo" @@ -71,8 +71,8 @@ type Agent struct { // File provider fileProvider fileProv.Provider - // Container provider - containerProvider containerProv.Provider + // Docker provider + dockerProvider dockerProv.Provider // Registry KV for heartbeat registration registryKV jetstream.KeyValue diff --git a/internal/api/container/gen/container.gen.go b/internal/api/container/gen/container.gen.go deleted file mode 100644 index 0f22430f..00000000 --- a/internal/api/container/gen/container.gen.go +++ /dev/null @@ -1,1351 +0,0 @@ -// Package gen provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT. -package gen - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - "github.com/labstack/echo/v4" - "github.com/oapi-codegen/runtime" - strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" - openapi_types "github.com/oapi-codegen/runtime/types" - externalRef0 "github.com/retr0h/osapi/internal/api/common/gen" -) - -const ( - BearerAuthScopes = "BearerAuth.Scopes" -) - -// Defines values for GetNodeContainerParamsState. -const ( - All GetNodeContainerParamsState = "all" - Running GetNodeContainerParamsState = "running" - Stopped GetNodeContainerParamsState = "stopped" -) - -// ContainerActionCollectionResponse defines model for ContainerActionCollectionResponse. -type ContainerActionCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerActionResultItem `json:"results"` -} - -// ContainerActionResultItem Result of a container lifecycle action. -type ContainerActionResultItem struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` - - // Id Container identifier. - Id *string `json:"id,omitempty"` - - // Message Status message. - Message *string `json:"message,omitempty"` -} - -// ContainerCreateRequest defines model for ContainerCreateRequest. -type ContainerCreateRequest struct { - // AutoStart Whether to start the container immediately after creation. Defaults to true. - AutoStart *bool `json:"auto_start,omitempty"` - - // Command Command to run in the container. - Command *[]string `json:"command,omitempty"` - - // Env Environment variables in KEY=VALUE format. - Env *[]string `json:"env,omitempty"` - - // Image Container image reference (e.g., "nginx:latest"). - Image string `json:"image" validate:"required,min=1"` - - // Name Optional name for the container. - Name *string `json:"name,omitempty"` - - // Ports Port mappings in host_port:container_port format. - Ports *[]string `json:"ports,omitempty"` - - // Volumes Volume mounts in host_path:container_path format. - Volumes *[]string `json:"volumes,omitempty"` -} - -// ContainerDetailCollectionResponse defines model for ContainerDetailCollectionResponse. -type ContainerDetailCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerDetailResponse `json:"results"` -} - -// ContainerDetailResponse Detailed information about a container. -type ContainerDetailResponse struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Created Container creation timestamp. - Created *string `json:"created,omitempty"` - - // Env Environment variables. - Env *[]string `json:"env,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Health Health check status if configured. - Health *string `json:"health,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` - - // Id Container identifier. - Id *string `json:"id,omitempty"` - - // Image Image used by the container. - Image *string `json:"image,omitempty"` - - // Mounts Volume mounts. - Mounts *[]string `json:"mounts,omitempty"` - - // Name Container name. - Name *string `json:"name,omitempty"` - - // NetworkSettings Network configuration. - NetworkSettings *map[string]string `json:"network_settings,omitempty"` - - // Ports Port mappings. - Ports *[]string `json:"ports,omitempty"` - - // State Current container state. - State *string `json:"state,omitempty"` -} - -// ContainerExecCollectionResponse defines model for ContainerExecCollectionResponse. -type ContainerExecCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerExecResultItem `json:"results"` -} - -// ContainerExecRequest defines model for ContainerExecRequest. -type ContainerExecRequest struct { - // Command Command to execute inside the container. - Command []string `json:"command" validate:"required,min=1"` - - // Env Additional environment variables in KEY=VALUE format. - Env *[]string `json:"env,omitempty"` - - // WorkingDir Working directory inside the container. - WorkingDir *string `json:"working_dir,omitempty"` -} - -// ContainerExecResultItem Result of a command execution inside a container. -type ContainerExecResultItem struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // ExitCode Exit code of the command. - ExitCode *int `json:"exit_code,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` - - // Stderr Standard error output of the command. - Stderr *string `json:"stderr,omitempty"` - - // Stdout Standard output of the command. - Stdout *string `json:"stdout,omitempty"` -} - -// ContainerListCollectionResponse defines model for ContainerListCollectionResponse. -type ContainerListCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerListItem `json:"results"` -} - -// ContainerListItem Container summary for list operations. -type ContainerListItem struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Containers List of containers on this agent. - Containers *[]ContainerSummary `json:"containers,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` -} - -// ContainerPullCollectionResponse defines model for ContainerPullCollectionResponse. -type ContainerPullCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerPullResultItem `json:"results"` -} - -// ContainerPullRequest defines model for ContainerPullRequest. -type ContainerPullRequest struct { - // Image Image reference to pull (e.g., "nginx:latest", "docker.io/library/alpine:3.18"). - Image string `json:"image" validate:"required,min=1"` -} - -// ContainerPullResultItem Result of an image pull operation. -type ContainerPullResultItem struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` - - // ImageId The pulled image ID. - ImageId *string `json:"image_id,omitempty"` - - // Size Image size in bytes. - Size *int64 `json:"size,omitempty"` - - // Tag The image tag that was pulled. - Tag *string `json:"tag,omitempty"` -} - -// ContainerResponse Summary information about a container. -type ContainerResponse struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Created Container creation timestamp. - Created *string `json:"created,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` - - // Id Container identifier. - Id *string `json:"id,omitempty"` - - // Image Image used by the container. - Image *string `json:"image,omitempty"` - - // Name Container name. - Name *string `json:"name,omitempty"` - - // State Current container state. - State *string `json:"state,omitempty"` -} - -// ContainerResultCollectionResponse defines model for ContainerResultCollectionResponse. -type ContainerResultCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerResponse `json:"results"` -} - -// ContainerStopRequest defines model for ContainerStopRequest. -type ContainerStopRequest struct { - // Timeout Seconds to wait before killing the container. Defaults to 10. - Timeout *int `json:"timeout,omitempty" validate:"omitempty,min=0,max=300"` -} - -// ContainerSummary Brief container summary. -type ContainerSummary struct { - // Created Container creation timestamp. - Created *string `json:"created,omitempty"` - - // Id Container identifier. - Id *string `json:"id,omitempty"` - - // Image Image used by the container. - Image *string `json:"image,omitempty"` - - // Name Container name. - Name *string `json:"name,omitempty"` - - // State Current container state. - State *string `json:"state,omitempty"` -} - -// ErrorResponse defines model for ErrorResponse. -type ErrorResponse = externalRef0.ErrorResponse - -// ContainerId defines model for ContainerId. -type ContainerId = string - -// Hostname defines model for Hostname. -type Hostname = string - -// GetNodeContainerParams defines parameters for GetNodeContainer. -type GetNodeContainerParams struct { - // State Filter containers by state. Defaults to "all". - State *GetNodeContainerParamsState `form:"state,omitempty" json:"state,omitempty" validate:"omitempty,oneof=running stopped all"` - - // Limit Maximum number of containers to return. - Limit *int `form:"limit,omitempty" json:"limit,omitempty" validate:"omitempty,min=1,max=100"` -} - -// GetNodeContainerParamsState defines parameters for GetNodeContainer. -type GetNodeContainerParamsState string - -// DeleteNodeContainerByIDParams defines parameters for DeleteNodeContainerByID. -type DeleteNodeContainerByIDParams struct { - // Force Force removal of a running container. - Force *bool `form:"force,omitempty" json:"force,omitempty" validate:"omitempty"` -} - -// PostNodeContainerJSONRequestBody defines body for PostNodeContainer for application/json ContentType. -type PostNodeContainerJSONRequestBody = ContainerCreateRequest - -// PostNodeContainerPullJSONRequestBody defines body for PostNodeContainerPull for application/json ContentType. -type PostNodeContainerPullJSONRequestBody = ContainerPullRequest - -// PostNodeContainerExecJSONRequestBody defines body for PostNodeContainerExec for application/json ContentType. -type PostNodeContainerExecJSONRequestBody = ContainerExecRequest - -// PostNodeContainerStopJSONRequestBody defines body for PostNodeContainerStop for application/json ContentType. -type PostNodeContainerStopJSONRequestBody = ContainerStopRequest - -// ServerInterface represents all server handlers. -type ServerInterface interface { - // List containers - // (GET /node/{hostname}/container) - GetNodeContainer(ctx echo.Context, hostname Hostname, params GetNodeContainerParams) error - // Create a container - // (POST /node/{hostname}/container) - PostNodeContainer(ctx echo.Context, hostname Hostname) error - // Pull a container image - // (POST /node/{hostname}/container/pull) - PostNodeContainerPull(ctx echo.Context, hostname Hostname) error - // Remove a container - // (DELETE /node/{hostname}/container/{id}) - DeleteNodeContainerByID(ctx echo.Context, hostname Hostname, id ContainerId, params DeleteNodeContainerByIDParams) error - // Inspect a container - // (GET /node/{hostname}/container/{id}) - GetNodeContainerByID(ctx echo.Context, hostname Hostname, id ContainerId) error - // Execute a command in a container - // (POST /node/{hostname}/container/{id}/exec) - PostNodeContainerExec(ctx echo.Context, hostname Hostname, id ContainerId) error - // Start a container - // (POST /node/{hostname}/container/{id}/start) - PostNodeContainerStart(ctx echo.Context, hostname Hostname, id ContainerId) error - // Stop a container - // (POST /node/{hostname}/container/{id}/stop) - PostNodeContainerStop(ctx echo.Context, hostname Hostname, id ContainerId) error -} - -// ServerInterfaceWrapper converts echo contexts to parameters. -type ServerInterfaceWrapper struct { - Handler ServerInterface -} - -// GetNodeContainer converts echo context to params. -func (w *ServerInterfaceWrapper) GetNodeContainer(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:read"}) - - // Parameter object where we will unmarshal all parameters from the context - var params GetNodeContainerParams - // ------------- Optional query parameter "state" ------------- - - err = runtime.BindQueryParameter("form", true, false, "state", ctx.QueryParams(), ¶ms.State) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter state: %s", err)) - } - - // ------------- Optional query parameter "limit" ------------- - - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetNodeContainer(ctx, hostname, params) - return err -} - -// PostNodeContainer converts echo context to params. -func (w *ServerInterfaceWrapper) PostNodeContainer(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:write"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostNodeContainer(ctx, hostname) - return err -} - -// PostNodeContainerPull converts echo context to params. -func (w *ServerInterfaceWrapper) PostNodeContainerPull(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:write"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostNodeContainerPull(ctx, hostname) - return err -} - -// DeleteNodeContainerByID converts echo context to params. -func (w *ServerInterfaceWrapper) DeleteNodeContainerByID(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - // ------------- Path parameter "id" ------------- - var id ContainerId - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:write"}) - - // Parameter object where we will unmarshal all parameters from the context - var params DeleteNodeContainerByIDParams - // ------------- Optional query parameter "force" ------------- - - err = runtime.BindQueryParameter("form", true, false, "force", ctx.QueryParams(), ¶ms.Force) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter force: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.DeleteNodeContainerByID(ctx, hostname, id, params) - return err -} - -// GetNodeContainerByID converts echo context to params. -func (w *ServerInterfaceWrapper) GetNodeContainerByID(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - // ------------- Path parameter "id" ------------- - var id ContainerId - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:read"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetNodeContainerByID(ctx, hostname, id) - return err -} - -// PostNodeContainerExec converts echo context to params. -func (w *ServerInterfaceWrapper) PostNodeContainerExec(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - // ------------- Path parameter "id" ------------- - var id ContainerId - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:execute"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostNodeContainerExec(ctx, hostname, id) - return err -} - -// PostNodeContainerStart converts echo context to params. -func (w *ServerInterfaceWrapper) PostNodeContainerStart(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - // ------------- Path parameter "id" ------------- - var id ContainerId - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:write"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostNodeContainerStart(ctx, hostname, id) - return err -} - -// PostNodeContainerStop converts echo context to params. -func (w *ServerInterfaceWrapper) PostNodeContainerStop(ctx echo.Context) error { - var err error - // ------------- Path parameter "hostname" ------------- - var hostname Hostname - - err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) - } - - // ------------- Path parameter "id" ------------- - var id ContainerId - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - ctx.Set(BearerAuthScopes, []string{"container:write"}) - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostNodeContainerStop(ctx, hostname, id) - return err -} - -// This is a simple interface which specifies echo.Route addition functions which -// are present on both echo.Echo and echo.Group, since we want to allow using -// either of them for path registration -type EchoRouter interface { - CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route -} - -// RegisterHandlers adds each server route to the EchoRouter. -func RegisterHandlers(router EchoRouter, si ServerInterface) { - RegisterHandlersWithBaseURL(router, si, "") -} - -// Registers handlers, and prepends BaseURL to the paths, so that the paths -// can be served under a prefix. -func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { - - wrapper := ServerInterfaceWrapper{ - Handler: si, - } - - router.GET(baseURL+"/node/:hostname/container", wrapper.GetNodeContainer) - router.POST(baseURL+"/node/:hostname/container", wrapper.PostNodeContainer) - router.POST(baseURL+"/node/:hostname/container/pull", wrapper.PostNodeContainerPull) - router.DELETE(baseURL+"/node/:hostname/container/:id", wrapper.DeleteNodeContainerByID) - router.GET(baseURL+"/node/:hostname/container/:id", wrapper.GetNodeContainerByID) - router.POST(baseURL+"/node/:hostname/container/:id/exec", wrapper.PostNodeContainerExec) - router.POST(baseURL+"/node/:hostname/container/:id/start", wrapper.PostNodeContainerStart) - router.POST(baseURL+"/node/:hostname/container/:id/stop", wrapper.PostNodeContainerStop) - -} - -type GetNodeContainerRequestObject struct { - Hostname Hostname `json:"hostname"` - Params GetNodeContainerParams -} - -type GetNodeContainerResponseObject interface { - VisitGetNodeContainerResponse(w http.ResponseWriter) error -} - -type GetNodeContainer200JSONResponse ContainerListCollectionResponse - -func (response GetNodeContainer200JSONResponse) VisitGetNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainer400JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainer400JSONResponse) VisitGetNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainer401JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainer401JSONResponse) VisitGetNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainer403JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainer403JSONResponse) VisitGetNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainer500JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainer500JSONResponse) VisitGetNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerRequestObject struct { - Hostname Hostname `json:"hostname"` - Body *PostNodeContainerJSONRequestBody -} - -type PostNodeContainerResponseObject interface { - VisitPostNodeContainerResponse(w http.ResponseWriter) error -} - -type PostNodeContainer202JSONResponse ContainerResultCollectionResponse - -func (response PostNodeContainer202JSONResponse) VisitPostNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainer400JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainer400JSONResponse) VisitPostNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainer401JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainer401JSONResponse) VisitPostNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainer403JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainer403JSONResponse) VisitPostNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainer500JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainer500JSONResponse) VisitPostNodeContainerResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerPullRequestObject struct { - Hostname Hostname `json:"hostname"` - Body *PostNodeContainerPullJSONRequestBody -} - -type PostNodeContainerPullResponseObject interface { - VisitPostNodeContainerPullResponse(w http.ResponseWriter) error -} - -type PostNodeContainerPull202JSONResponse ContainerPullCollectionResponse - -func (response PostNodeContainerPull202JSONResponse) VisitPostNodeContainerPullResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerPull400JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerPull400JSONResponse) VisitPostNodeContainerPullResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerPull401JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerPull401JSONResponse) VisitPostNodeContainerPullResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerPull403JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerPull403JSONResponse) VisitPostNodeContainerPullResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerPull500JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerPull500JSONResponse) VisitPostNodeContainerPullResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByIDRequestObject struct { - Hostname Hostname `json:"hostname"` - Id ContainerId `json:"id"` - Params DeleteNodeContainerByIDParams -} - -type DeleteNodeContainerByIDResponseObject interface { - VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error -} - -type DeleteNodeContainerByID202JSONResponse ContainerActionCollectionResponse - -func (response DeleteNodeContainerByID202JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByID400JSONResponse externalRef0.ErrorResponse - -func (response DeleteNodeContainerByID400JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByID401JSONResponse externalRef0.ErrorResponse - -func (response DeleteNodeContainerByID401JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByID403JSONResponse externalRef0.ErrorResponse - -func (response DeleteNodeContainerByID403JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByID404JSONResponse externalRef0.ErrorResponse - -func (response DeleteNodeContainerByID404JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - -type DeleteNodeContainerByID500JSONResponse externalRef0.ErrorResponse - -func (response DeleteNodeContainerByID500JSONResponse) VisitDeleteNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByIDRequestObject struct { - Hostname Hostname `json:"hostname"` - Id ContainerId `json:"id"` -} - -type GetNodeContainerByIDResponseObject interface { - VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error -} - -type GetNodeContainerByID200JSONResponse ContainerDetailCollectionResponse - -func (response GetNodeContainerByID200JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByID400JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainerByID400JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByID401JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainerByID401JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByID403JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainerByID403JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByID404JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainerByID404JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - -type GetNodeContainerByID500JSONResponse externalRef0.ErrorResponse - -func (response GetNodeContainerByID500JSONResponse) VisitGetNodeContainerByIDResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExecRequestObject struct { - Hostname Hostname `json:"hostname"` - Id ContainerId `json:"id"` - Body *PostNodeContainerExecJSONRequestBody -} - -type PostNodeContainerExecResponseObject interface { - VisitPostNodeContainerExecResponse(w http.ResponseWriter) error -} - -type PostNodeContainerExec202JSONResponse ContainerExecCollectionResponse - -func (response PostNodeContainerExec202JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExec400JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerExec400JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExec401JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerExec401JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExec403JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerExec403JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExec404JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerExec404JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerExec500JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerExec500JSONResponse) VisitPostNodeContainerExecResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStartRequestObject struct { - Hostname Hostname `json:"hostname"` - Id ContainerId `json:"id"` -} - -type PostNodeContainerStartResponseObject interface { - VisitPostNodeContainerStartResponse(w http.ResponseWriter) error -} - -type PostNodeContainerStart202JSONResponse ContainerActionCollectionResponse - -func (response PostNodeContainerStart202JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStart400JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStart400JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStart401JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStart401JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStart403JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStart403JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStart404JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStart404JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStart500JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStart500JSONResponse) VisitPostNodeContainerStartResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStopRequestObject struct { - Hostname Hostname `json:"hostname"` - Id ContainerId `json:"id"` - Body *PostNodeContainerStopJSONRequestBody -} - -type PostNodeContainerStopResponseObject interface { - VisitPostNodeContainerStopResponse(w http.ResponseWriter) error -} - -type PostNodeContainerStop202JSONResponse ContainerActionCollectionResponse - -func (response PostNodeContainerStop202JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(202) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStop400JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStop400JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStop401JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStop401JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStop403JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStop403JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(403) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStop404JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStop404JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - -type PostNodeContainerStop500JSONResponse externalRef0.ErrorResponse - -func (response PostNodeContainerStop500JSONResponse) VisitPostNodeContainerStopResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -// StrictServerInterface represents all server handlers. -type StrictServerInterface interface { - // List containers - // (GET /node/{hostname}/container) - GetNodeContainer(ctx context.Context, request GetNodeContainerRequestObject) (GetNodeContainerResponseObject, error) - // Create a container - // (POST /node/{hostname}/container) - PostNodeContainer(ctx context.Context, request PostNodeContainerRequestObject) (PostNodeContainerResponseObject, error) - // Pull a container image - // (POST /node/{hostname}/container/pull) - PostNodeContainerPull(ctx context.Context, request PostNodeContainerPullRequestObject) (PostNodeContainerPullResponseObject, error) - // Remove a container - // (DELETE /node/{hostname}/container/{id}) - DeleteNodeContainerByID(ctx context.Context, request DeleteNodeContainerByIDRequestObject) (DeleteNodeContainerByIDResponseObject, error) - // Inspect a container - // (GET /node/{hostname}/container/{id}) - GetNodeContainerByID(ctx context.Context, request GetNodeContainerByIDRequestObject) (GetNodeContainerByIDResponseObject, error) - // Execute a command in a container - // (POST /node/{hostname}/container/{id}/exec) - PostNodeContainerExec(ctx context.Context, request PostNodeContainerExecRequestObject) (PostNodeContainerExecResponseObject, error) - // Start a container - // (POST /node/{hostname}/container/{id}/start) - PostNodeContainerStart(ctx context.Context, request PostNodeContainerStartRequestObject) (PostNodeContainerStartResponseObject, error) - // Stop a container - // (POST /node/{hostname}/container/{id}/stop) - PostNodeContainerStop(ctx context.Context, request PostNodeContainerStopRequestObject) (PostNodeContainerStopResponseObject, error) -} - -type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc -type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc - -func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { - return &strictHandler{ssi: ssi, middlewares: middlewares} -} - -type strictHandler struct { - ssi StrictServerInterface - middlewares []StrictMiddlewareFunc -} - -// GetNodeContainer operation middleware -func (sh *strictHandler) GetNodeContainer(ctx echo.Context, hostname Hostname, params GetNodeContainerParams) error { - var request GetNodeContainerRequestObject - - request.Hostname = hostname - request.Params = params - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.GetNodeContainer(ctx.Request().Context(), request.(GetNodeContainerRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetNodeContainer") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(GetNodeContainerResponseObject); ok { - return validResponse.VisitGetNodeContainerResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostNodeContainer operation middleware -func (sh *strictHandler) PostNodeContainer(ctx echo.Context, hostname Hostname) error { - var request PostNodeContainerRequestObject - - request.Hostname = hostname - - var body PostNodeContainerJSONRequestBody - if err := ctx.Bind(&body); err != nil { - return err - } - request.Body = &body - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostNodeContainer(ctx.Request().Context(), request.(PostNodeContainerRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostNodeContainer") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(PostNodeContainerResponseObject); ok { - return validResponse.VisitPostNodeContainerResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostNodeContainerPull operation middleware -func (sh *strictHandler) PostNodeContainerPull(ctx echo.Context, hostname Hostname) error { - var request PostNodeContainerPullRequestObject - - request.Hostname = hostname - - var body PostNodeContainerPullJSONRequestBody - if err := ctx.Bind(&body); err != nil { - return err - } - request.Body = &body - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostNodeContainerPull(ctx.Request().Context(), request.(PostNodeContainerPullRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostNodeContainerPull") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(PostNodeContainerPullResponseObject); ok { - return validResponse.VisitPostNodeContainerPullResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// DeleteNodeContainerByID operation middleware -func (sh *strictHandler) DeleteNodeContainerByID(ctx echo.Context, hostname Hostname, id ContainerId, params DeleteNodeContainerByIDParams) error { - var request DeleteNodeContainerByIDRequestObject - - request.Hostname = hostname - request.Id = id - request.Params = params - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.DeleteNodeContainerByID(ctx.Request().Context(), request.(DeleteNodeContainerByIDRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "DeleteNodeContainerByID") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(DeleteNodeContainerByIDResponseObject); ok { - return validResponse.VisitDeleteNodeContainerByIDResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// GetNodeContainerByID operation middleware -func (sh *strictHandler) GetNodeContainerByID(ctx echo.Context, hostname Hostname, id ContainerId) error { - var request GetNodeContainerByIDRequestObject - - request.Hostname = hostname - request.Id = id - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.GetNodeContainerByID(ctx.Request().Context(), request.(GetNodeContainerByIDRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetNodeContainerByID") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(GetNodeContainerByIDResponseObject); ok { - return validResponse.VisitGetNodeContainerByIDResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostNodeContainerExec operation middleware -func (sh *strictHandler) PostNodeContainerExec(ctx echo.Context, hostname Hostname, id ContainerId) error { - var request PostNodeContainerExecRequestObject - - request.Hostname = hostname - request.Id = id - - var body PostNodeContainerExecJSONRequestBody - if err := ctx.Bind(&body); err != nil { - return err - } - request.Body = &body - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostNodeContainerExec(ctx.Request().Context(), request.(PostNodeContainerExecRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostNodeContainerExec") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(PostNodeContainerExecResponseObject); ok { - return validResponse.VisitPostNodeContainerExecResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostNodeContainerStart operation middleware -func (sh *strictHandler) PostNodeContainerStart(ctx echo.Context, hostname Hostname, id ContainerId) error { - var request PostNodeContainerStartRequestObject - - request.Hostname = hostname - request.Id = id - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostNodeContainerStart(ctx.Request().Context(), request.(PostNodeContainerStartRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostNodeContainerStart") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(PostNodeContainerStartResponseObject); ok { - return validResponse.VisitPostNodeContainerStartResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostNodeContainerStop operation middleware -func (sh *strictHandler) PostNodeContainerStop(ctx echo.Context, hostname Hostname, id ContainerId) error { - var request PostNodeContainerStopRequestObject - - request.Hostname = hostname - request.Id = id - - var body PostNodeContainerStopJSONRequestBody - if err := ctx.Bind(&body); err != nil { - return err - } - request.Body = &body - - handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostNodeContainerStop(ctx.Request().Context(), request.(PostNodeContainerStopRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostNodeContainerStop") - } - - response, err := handler(ctx, request) - - if err != nil { - return err - } else if validResponse, ok := response.(PostNodeContainerStopResponseObject); ok { - return validResponse.VisitPostNodeContainerStopResponse(ctx.Response()) - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} diff --git a/internal/api/container/container.go b/internal/api/docker/container.go similarity index 96% rename from internal/api/container/container.go rename to internal/api/docker/container.go index 872c6ead..affe0702 100644 --- a/internal/api/container/container.go +++ b/internal/api/docker/container.go @@ -24,7 +24,7 @@ package container import ( "log/slog" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job/client" ) diff --git a/internal/api/container/container_create.go b/internal/api/docker/container_create.go similarity index 78% rename from internal/api/container/container_create.go rename to internal/api/docker/container_create.go index d46b1973..b147458e 100644 --- a/internal/api/container/container_create.go +++ b/internal/api/docker/container_create.go @@ -27,25 +27,25 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/validation" ) -// PostNodeContainer creates a container on a target node. -func (s *Container) PostNodeContainer( +// PostNodeContainerDocker creates a container on a target node. +func (s *Container) PostNodeContainerDocker( ctx context.Context, - request gen.PostNodeContainerRequestObject, -) (gen.PostNodeContainerResponseObject, error) { + request gen.PostNodeContainerDockerRequestObject, +) (gen.PostNodeContainerDockerResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.PostNodeContainer400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDocker400JSONResponse{Error: &errMsg}, nil } if errMsg, ok := validation.Struct(request.Body); !ok { - return gen.PostNodeContainer400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDocker400JSONResponse{Error: &errMsg}, nil } - data := &job.ContainerCreateData{ + data := &job.DockerCreateData{ Image: request.Body.Image, Command: ptrToSlice(request.Body.Command), Env: envSliceToMap(request.Body.Env), @@ -68,10 +68,10 @@ func (s *Container) PostNodeContainer( slog.String("target", hostname), ) - resp, err := s.JobClient.ModifyContainerCreate(ctx, hostname, data) + resp, err := s.JobClient.ModifyDockerCreate(ctx, hostname, data) if err != nil { errMsg := err.Error() - return gen.PostNodeContainer500JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDocker500JSONResponse{Error: &errMsg}, nil } var containerResp struct { @@ -85,9 +85,9 @@ func (s *Container) PostNodeContainer( id := containerResp.ID changed := resp.Changed - return gen.PostNodeContainer202JSONResponse{ + return gen.PostNodeContainerDocker202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerResponse{ + Results: []gen.DockerResponse{ { Hostname: resp.Hostname, Id: stringPtrOrNil(id), diff --git a/internal/api/container/container_create_public_test.go b/internal/api/docker/container_create_public_test.go similarity index 77% rename from internal/api/container/container_create_public_test.go rename to internal/api/docker/container_create_public_test.go index 1c455ac5..fc76791f 100644 --- a/internal/api/container/container_create_public_test.go +++ b/internal/api/docker/container_create_public_test.go @@ -35,8 +35,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -76,25 +76,25 @@ func (s *ContainerCreatePublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { +func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerDocker() { tests := []struct { name string - request gen.PostNodeContainerRequestObject + request gen.PostNodeContainerDockerRequestObject setupMock func() - validateFunc func(resp gen.PostNodeContainerResponseObject) + validateFunc func(resp gen.PostNodeContainerDockerResponseObject) }{ { name: "success", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "nginx:latest", Name: strPtr("my-nginx"), }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerCreate( + ModifyDockerCreate( gomock.Any(), "server1", gomock.Any(), @@ -106,8 +106,8 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { Data: json.RawMessage(`{"id":"abc123"}`), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - r, ok := resp.(gen.PostNodeContainer202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + r, ok := resp.(gen.PostNodeContainerDocker202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -119,15 +119,15 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { }, { name: "validation error empty hostname", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - r, ok := resp.(gen.PostNodeContainer400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + r, ok := resp.(gen.PostNodeContainerDocker400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -135,31 +135,31 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { }, { name: "body validation error empty image", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "", }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - r, ok := resp.(gen.PostNodeContainer400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + r, ok := resp.(gen.PostNodeContainerDocker400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) }, }, { name: "success with explicit auto_start false", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "nginx:latest", AutoStart: boolPtr(false), }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerCreate( + ModifyDockerCreate( gomock.Any(), "server1", gomock.Any(), @@ -171,8 +171,8 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { Data: json.RawMessage(`{"id":"xyz789"}`), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - r, ok := resp.(gen.PostNodeContainer202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + r, ok := resp.(gen.PostNodeContainerDocker202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -184,15 +184,15 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { }, { name: "success with nil response data", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerCreate( + ModifyDockerCreate( gomock.Any(), "server1", gomock.Any(), @@ -204,8 +204,8 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { Data: nil, }, nil) }, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - r, ok := resp.(gen.PostNodeContainer202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + r, ok := resp.(gen.PostNodeContainerDocker202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -216,23 +216,23 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { }, { name: "job client error", - request: gen.PostNodeContainerRequestObject{ + request: gen.PostNodeContainerDockerRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerJSONRequestBody{ + Body: &gen.PostNodeContainerDockerJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerCreate( + ModifyDockerCreate( gomock.Any(), "server1", gomock.Any(), ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.PostNodeContainerResponseObject) { - _, ok := resp.(gen.PostNodeContainer500JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerResponseObject) { + _, ok := resp.(gen.PostNodeContainerDocker500JSONResponse) s.True(ok) }, }, @@ -242,14 +242,14 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainer() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.PostNodeContainer(s.ctx, tt.request) + resp, err := s.handler.PostNodeContainerDocker(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerValidationHTTP() { +func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerDockerValidationHTTP() { tests := []struct { name string path string @@ -260,12 +260,12 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerValidationHTTP() { }{ { name: "when valid request", - path: "/node/server1/container", + path: "/node/server1/container/docker", body: `{"image":"nginx:latest"}`, setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerCreate(gomock.Any(), "server1", gomock.Any()). + ModifyDockerCreate(gomock.Any(), "server1", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -279,7 +279,7 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerValidationHTTP() { }, { name: "when missing image", - path: "/node/server1/container", + path: "/node/server1/container/docker", body: `{}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) @@ -289,7 +289,7 @@ func (s *ContainerCreatePublicTestSuite) TestPostNodeContainerValidationHTTP() { }, { name: "when target agent not found", - path: "/node/nonexistent/container", + path: "/node/nonexistent/container/docker", body: `{"image":"nginx:latest"}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) diff --git a/internal/api/container/container_exec.go b/internal/api/docker/container_exec.go similarity index 76% rename from internal/api/container/container_exec.go rename to internal/api/docker/container_exec.go index a7c78d0a..c55a2aa2 100644 --- a/internal/api/container/container_exec.go +++ b/internal/api/docker/container_exec.go @@ -27,28 +27,28 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/validation" ) -// PostNodeContainerExec executes a command in a container on a target node. -func (s *Container) PostNodeContainerExec( +// PostNodeContainerDockerExec executes a command in a container on a target node. +func (s *Container) PostNodeContainerDockerExec( ctx context.Context, - request gen.PostNodeContainerExecRequestObject, -) (gen.PostNodeContainerExecResponseObject, error) { + request gen.PostNodeContainerDockerExecRequestObject, +) (gen.PostNodeContainerDockerExecResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.PostNodeContainerExec400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerExec400JSONResponse{Error: &errMsg}, nil } if errMsg, ok := validation.Struct(request.Body); !ok { - return gen.PostNodeContainerExec400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerExec400JSONResponse{Error: &errMsg}, nil } hostname := request.Hostname id := request.Id - data := &job.ContainerExecData{ + data := &job.DockerExecData{ Command: request.Body.Command, Env: envSliceToMap(request.Body.Env), } @@ -62,10 +62,10 @@ func (s *Container) PostNodeContainerExec( slog.Any("command", data.Command), ) - resp, err := s.JobClient.ModifyContainerExec(ctx, hostname, id, data) + resp, err := s.JobClient.ModifyDockerExec(ctx, hostname, id, data) if err != nil { errMsg := err.Error() - return gen.PostNodeContainerExec500JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerExec500JSONResponse{Error: &errMsg}, nil } var execResult struct { @@ -83,9 +83,9 @@ func (s *Container) PostNodeContainerExec( stderr := execResult.Stderr exitCode := execResult.ExitCode - return gen.PostNodeContainerExec202JSONResponse{ + return gen.PostNodeContainerDockerExec202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerExecResultItem{ + Results: []gen.DockerExecResultItem{ { Hostname: resp.Hostname, Stdout: &stdout, diff --git a/internal/api/container/container_exec_public_test.go b/internal/api/docker/container_exec_public_test.go similarity index 77% rename from internal/api/container/container_exec_public_test.go rename to internal/api/docker/container_exec_public_test.go index 8e2cedb1..ed829136 100644 --- a/internal/api/container/container_exec_public_test.go +++ b/internal/api/docker/container_exec_public_test.go @@ -35,8 +35,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -76,25 +76,25 @@ func (s *ContainerExecPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { +func (s *ContainerExecPublicTestSuite) TestPostNodeContainerDockerExec() { tests := []struct { name string - request gen.PostNodeContainerExecRequestObject + request gen.PostNodeContainerDockerExecRequestObject setupMock func() - validateFunc func(resp gen.PostNodeContainerExecResponseObject) + validateFunc func(resp gen.PostNodeContainerDockerExecResponseObject) }{ { name: "success", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{"ls", "-la"}, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerExec( + ModifyDockerExec( gomock.Any(), "server1", "abc123", @@ -109,8 +109,8 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { ), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - r, ok := resp.(gen.PostNodeContainerExec202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerExec202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -124,16 +124,16 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { }, { name: "validation error empty hostname", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{"ls"}, }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - r, ok := resp.(gen.PostNodeContainerExec400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerExec400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -141,33 +141,33 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { }, { name: "body validation error empty command", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{}, }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - r, ok := resp.(gen.PostNodeContainerExec400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerExec400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) }, }, { name: "success with working dir", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{"ls", "-la"}, WorkingDir: strPtr("/app"), }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerExec( + ModifyDockerExec( gomock.Any(), "server1", "abc123", @@ -182,8 +182,8 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { ), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - r, ok := resp.(gen.PostNodeContainerExec202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerExec202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -197,16 +197,16 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { }, { name: "success with nil response data", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{"ls"}, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerExec( + ModifyDockerExec( gomock.Any(), "server1", "abc123", @@ -219,8 +219,8 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { Data: nil, }, nil) }, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - r, ok := resp.(gen.PostNodeContainerExec202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerExec202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -236,16 +236,16 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { }, { name: "job client error", - request: gen.PostNodeContainerExecRequestObject{ + request: gen.PostNodeContainerDockerExecRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerExecJSONRequestBody{ + Body: &gen.PostNodeContainerDockerExecJSONRequestBody{ Command: []string{"ls"}, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerExec( + ModifyDockerExec( gomock.Any(), "server1", "abc123", @@ -253,8 +253,8 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.PostNodeContainerExecResponseObject) { - _, ok := resp.(gen.PostNodeContainerExec500JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerExecResponseObject) { + _, ok := resp.(gen.PostNodeContainerDockerExec500JSONResponse) s.True(ok) }, }, @@ -264,14 +264,14 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExec() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.PostNodeContainerExec(s.ctx, tt.request) + resp, err := s.handler.PostNodeContainerDockerExec(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExecValidationHTTP() { +func (s *ContainerExecPublicTestSuite) TestPostNodeContainerDockerExecValidationHTTP() { tests := []struct { name string path string @@ -282,12 +282,12 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExecValidationHTTP() }{ { name: "when valid request", - path: "/node/server1/container/abc123/exec", + path: "/node/server1/container/docker/abc123/exec", body: `{"command":["ls","-la"]}`, setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerExec(gomock.Any(), "server1", "abc123", gomock.Any()). + ModifyDockerExec(gomock.Any(), "server1", "abc123", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -301,7 +301,7 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExecValidationHTTP() }, { name: "when missing command", - path: "/node/server1/container/abc123/exec", + path: "/node/server1/container/docker/abc123/exec", body: `{}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) @@ -311,7 +311,7 @@ func (s *ContainerExecPublicTestSuite) TestPostNodeContainerExecValidationHTTP() }, { name: "when target agent not found", - path: "/node/nonexistent/container/abc123/exec", + path: "/node/nonexistent/container/docker/abc123/exec", body: `{"command":["ls"]}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) diff --git a/internal/api/container/container_inspect.go b/internal/api/docker/container_inspect.go similarity index 85% rename from internal/api/container/container_inspect.go rename to internal/api/docker/container_inspect.go index f16b3529..9202629d 100644 --- a/internal/api/container/container_inspect.go +++ b/internal/api/docker/container_inspect.go @@ -27,16 +27,16 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" ) -// GetNodeContainerByID inspects a container on a target node. -func (s *Container) GetNodeContainerByID( +// GetNodeContainerDockerByID inspects a container on a target node. +func (s *Container) GetNodeContainerDockerByID( ctx context.Context, - request gen.GetNodeContainerByIDRequestObject, -) (gen.GetNodeContainerByIDResponseObject, error) { + request gen.GetNodeContainerDockerByIDRequestObject, +) (gen.GetNodeContainerDockerByIDResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.GetNodeContainerByID400JSONResponse{Error: &errMsg}, nil + return gen.GetNodeContainerDockerByID400JSONResponse{Error: &errMsg}, nil } hostname := request.Hostname @@ -47,10 +47,10 @@ func (s *Container) GetNodeContainerByID( slog.String("id", id), ) - resp, err := s.JobClient.QueryContainerInspect(ctx, hostname, id) + resp, err := s.JobClient.QueryDockerInspect(ctx, hostname, id) if err != nil { errMsg := err.Error() - return gen.GetNodeContainerByID500JSONResponse{Error: &errMsg}, nil + return gen.GetNodeContainerDockerByID500JSONResponse{Error: &errMsg}, nil } var detail struct { @@ -97,9 +97,9 @@ func (s *Container) GetNodeContainerByID( jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed - return gen.GetNodeContainerByID200JSONResponse{ + return gen.GetNodeContainerDockerByID200JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerDetailResponse{ + Results: []gen.DockerDetailResponse{ { Hostname: resp.Hostname, Id: stringPtrOrNil(detail.ID), diff --git a/internal/api/container/container_inspect_public_test.go b/internal/api/docker/container_inspect_public_test.go similarity index 81% rename from internal/api/container/container_inspect_public_test.go rename to internal/api/docker/container_inspect_public_test.go index 4111ab8a..85b1afa4 100644 --- a/internal/api/container/container_inspect_public_test.go +++ b/internal/api/docker/container_inspect_public_test.go @@ -34,8 +34,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -75,22 +75,22 @@ func (s *ContainerInspectPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByID() { +func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerDockerByID() { tests := []struct { name string - request gen.GetNodeContainerByIDRequestObject + request gen.GetNodeContainerDockerByIDRequestObject setupMock func() - validateFunc func(resp gen.GetNodeContainerByIDResponseObject) + validateFunc func(resp gen.GetNodeContainerDockerByIDResponseObject) }{ { name: "success", - request: gen.GetNodeContainerByIDRequestObject{ + request: gen.GetNodeContainerDockerByIDRequestObject{ Hostname: "server1", Id: "abc123", }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerInspect( + QueryDockerInspect( gomock.Any(), "server1", "abc123", @@ -107,8 +107,8 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByID() { }`), }, nil) }, - validateFunc: func(resp gen.GetNodeContainerByIDResponseObject) { - r, ok := resp.(gen.GetNodeContainerByID200JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerByIDResponseObject) { + r, ok := resp.(gen.GetNodeContainerDockerByID200JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -123,13 +123,13 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByID() { }, { name: "validation error empty hostname", - request: gen.GetNodeContainerByIDRequestObject{ + request: gen.GetNodeContainerDockerByIDRequestObject{ Hostname: "", Id: "abc123", }, setupMock: func() {}, - validateFunc: func(resp gen.GetNodeContainerByIDResponseObject) { - r, ok := resp.(gen.GetNodeContainerByID400JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerByIDResponseObject) { + r, ok := resp.(gen.GetNodeContainerDockerByID400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -137,21 +137,21 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByID() { }, { name: "job client error", - request: gen.GetNodeContainerByIDRequestObject{ + request: gen.GetNodeContainerDockerByIDRequestObject{ Hostname: "server1", Id: "abc123", }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerInspect( + QueryDockerInspect( gomock.Any(), "server1", "abc123", ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.GetNodeContainerByIDResponseObject) { - _, ok := resp.(gen.GetNodeContainerByID500JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerByIDResponseObject) { + _, ok := resp.(gen.GetNodeContainerDockerByID500JSONResponse) s.True(ok) }, }, @@ -161,14 +161,14 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByID() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.GetNodeContainerByID(s.ctx, tt.request) + resp, err := s.handler.GetNodeContainerDockerByID(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByIDValidationHTTP() { +func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerDockerByIDValidationHTTP() { tests := []struct { name string path string @@ -178,11 +178,11 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByIDValidationHTTP }{ { name: "when valid request", - path: "/node/server1/container/abc123", + path: "/node/server1/container/docker/abc123", setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - QueryContainerInspect(gomock.Any(), "server1", "abc123"). + QueryDockerInspect(gomock.Any(), "server1", "abc123"). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -197,7 +197,7 @@ func (s *ContainerInspectPublicTestSuite) TestGetNodeContainerByIDValidationHTTP }, { name: "when target agent not found", - path: "/node/nonexistent/container/abc123", + path: "/node/nonexistent/container/docker/abc123", setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) }, diff --git a/internal/api/container/container_list.go b/internal/api/docker/container_list.go similarity index 77% rename from internal/api/container/container_list.go rename to internal/api/docker/container_list.go index c5ced0c9..b4e4dbb4 100644 --- a/internal/api/container/container_list.go +++ b/internal/api/docker/container_list.go @@ -27,25 +27,25 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/validation" ) -// GetNodeContainer lists containers on a target node. -func (s *Container) GetNodeContainer( +// GetNodeContainerDocker lists containers on a target node. +func (s *Container) GetNodeContainerDocker( ctx context.Context, - request gen.GetNodeContainerRequestObject, -) (gen.GetNodeContainerResponseObject, error) { + request gen.GetNodeContainerDockerRequestObject, +) (gen.GetNodeContainerDockerResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.GetNodeContainer400JSONResponse{Error: &errMsg}, nil + return gen.GetNodeContainerDocker400JSONResponse{Error: &errMsg}, nil } if errMsg, ok := validation.Struct(request.Params); !ok { - return gen.GetNodeContainer400JSONResponse{Error: &errMsg}, nil + return gen.GetNodeContainerDocker400JSONResponse{Error: &errMsg}, nil } - data := &job.ContainerListData{} + data := &job.DockerListData{} if request.Params.State != nil { data.State = string(*request.Params.State) } @@ -60,10 +60,10 @@ func (s *Container) GetNodeContainer( slog.String("state", data.State), ) - resp, err := s.JobClient.QueryContainerList(ctx, hostname, data) + resp, err := s.JobClient.QueryDockerList(ctx, hostname, data) if err != nil { errMsg := err.Error() - return gen.GetNodeContainer500JSONResponse{Error: &errMsg}, nil + return gen.GetNodeContainerDocker500JSONResponse{Error: &errMsg}, nil } var containers []struct { @@ -77,14 +77,14 @@ func (s *Container) GetNodeContainer( _ = json.Unmarshal(resp.Data, &containers) } - var summaries []gen.ContainerSummary + var summaries []gen.DockerSummary for _, c := range containers { id := c.ID name := c.Name image := c.Image state := c.State created := c.Created - summaries = append(summaries, gen.ContainerSummary{ + summaries = append(summaries, gen.DockerSummary{ Id: &id, Name: &name, Image: &image, @@ -96,9 +96,9 @@ func (s *Container) GetNodeContainer( jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed - return gen.GetNodeContainer200JSONResponse{ + return gen.GetNodeContainerDocker200JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerListItem{ + Results: []gen.DockerListItem{ { Hostname: resp.Hostname, Containers: &summaries, diff --git a/internal/api/container/container_list_public_test.go b/internal/api/docker/container_list_public_test.go similarity index 76% rename from internal/api/container/container_list_public_test.go rename to internal/api/docker/container_list_public_test.go index df7dcc0b..9f251bdb 100644 --- a/internal/api/container/container_list_public_test.go +++ b/internal/api/docker/container_list_public_test.go @@ -34,8 +34,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -75,26 +75,26 @@ func (s *ContainerListPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { +func (s *ContainerListPublicTestSuite) TestGetNodeContainerDocker() { stateAll := gen.All tests := []struct { name string - request gen.GetNodeContainerRequestObject + request gen.GetNodeContainerDockerRequestObject setupMock func() - validateFunc func(resp gen.GetNodeContainerResponseObject) + validateFunc func(resp gen.GetNodeContainerDockerResponseObject) }{ { name: "success", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{ + Params: gen.GetNodeContainerDockerParams{ State: &stateAll, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerList( + QueryDockerList( gomock.Any(), "server1", gomock.Any(), @@ -107,8 +107,8 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { ), }, nil) }, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer200JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker200JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -121,13 +121,13 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "validation error empty hostname", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "", - Params: gen.GetNodeContainerParams{}, + Params: gen.GetNodeContainerDockerParams{}, }, setupMock: func() {}, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer400JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -135,15 +135,15 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "validation error invalid limit", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{ + Params: gen.GetNodeContainerDockerParams{ Limit: intPtr(0), }, }, setupMock: func() {}, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer400JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "Limit") @@ -151,16 +151,16 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "success with limit param", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{ + Params: gen.GetNodeContainerDockerParams{ State: &stateAll, Limit: intPtr(5), }, }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerList( + QueryDockerList( gomock.Any(), "server1", gomock.Any(), @@ -173,8 +173,8 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { ), }, nil) }, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer200JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker200JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -188,15 +188,15 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "success with nil response data", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{ + Params: gen.GetNodeContainerDockerParams{ State: &stateAll, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerList( + QueryDockerList( gomock.Any(), "server1", gomock.Any(), @@ -207,8 +207,8 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { Data: nil, }, nil) }, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer200JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker200JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -218,15 +218,15 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "success with empty created field", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{ + Params: gen.GetNodeContainerDockerParams{ State: &stateAll, }, }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerList( + QueryDockerList( gomock.Any(), "server1", gomock.Any(), @@ -239,8 +239,8 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { ), }, nil) }, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - r, ok := resp.(gen.GetNodeContainer200JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + r, ok := resp.(gen.GetNodeContainerDocker200JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Require().NotNil(r.Results[0].Containers) @@ -252,21 +252,21 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { }, { name: "job client error", - request: gen.GetNodeContainerRequestObject{ + request: gen.GetNodeContainerDockerRequestObject{ Hostname: "server1", - Params: gen.GetNodeContainerParams{}, + Params: gen.GetNodeContainerDockerParams{}, }, setupMock: func() { s.mockJobClient.EXPECT(). - QueryContainerList( + QueryDockerList( gomock.Any(), "server1", gomock.Any(), ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.GetNodeContainerResponseObject) { - _, ok := resp.(gen.GetNodeContainer500JSONResponse) + validateFunc: func(resp gen.GetNodeContainerDockerResponseObject) { + _, ok := resp.(gen.GetNodeContainerDocker500JSONResponse) s.True(ok) }, }, @@ -276,14 +276,14 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainer() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.GetNodeContainer(s.ctx, tt.request) + resp, err := s.handler.GetNodeContainerDocker(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerListPublicTestSuite) TestGetNodeContainerValidationHTTP() { +func (s *ContainerListPublicTestSuite) TestGetNodeContainerDockerValidationHTTP() { tests := []struct { name string path string @@ -293,11 +293,11 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainerValidationHTTP() { }{ { name: "when valid request", - path: "/node/server1/container", + path: "/node/server1/container/docker", setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - QueryContainerList(gomock.Any(), "server1", gomock.Any()). + QueryDockerList(gomock.Any(), "server1", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -310,7 +310,7 @@ func (s *ContainerListPublicTestSuite) TestGetNodeContainerValidationHTTP() { }, { name: "when target agent not found", - path: "/node/nonexistent/container", + path: "/node/nonexistent/container/docker", setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) }, diff --git a/internal/api/container/container_pull.go b/internal/api/docker/container_pull.go similarity index 76% rename from internal/api/container/container_pull.go rename to internal/api/docker/container_pull.go index 079c0ca6..f42963c6 100644 --- a/internal/api/container/container_pull.go +++ b/internal/api/docker/container_pull.go @@ -27,25 +27,25 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/validation" ) -// PostNodeContainerPull pulls a container image on a target node. -func (s *Container) PostNodeContainerPull( +// PostNodeContainerDockerPull pulls a container image on a target node. +func (s *Container) PostNodeContainerDockerPull( ctx context.Context, - request gen.PostNodeContainerPullRequestObject, -) (gen.PostNodeContainerPullResponseObject, error) { + request gen.PostNodeContainerDockerPullRequestObject, +) (gen.PostNodeContainerDockerPullResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.PostNodeContainerPull400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerPull400JSONResponse{Error: &errMsg}, nil } if errMsg, ok := validation.Struct(request.Body); !ok { - return gen.PostNodeContainerPull400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerPull400JSONResponse{Error: &errMsg}, nil } - data := &job.ContainerPullData{ + data := &job.DockerPullData{ Image: request.Body.Image, } @@ -56,10 +56,10 @@ func (s *Container) PostNodeContainerPull( slog.String("image", data.Image), ) - resp, err := s.JobClient.ModifyContainerPull(ctx, hostname, data) + resp, err := s.JobClient.ModifyDockerPull(ctx, hostname, data) if err != nil { errMsg := err.Error() - return gen.PostNodeContainerPull500JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerPull500JSONResponse{Error: &errMsg}, nil } var pullResult struct { @@ -74,9 +74,9 @@ func (s *Container) PostNodeContainerPull( jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed - return gen.PostNodeContainerPull202JSONResponse{ + return gen.PostNodeContainerDockerPull202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerPullResultItem{ + Results: []gen.DockerPullResultItem{ { Hostname: resp.Hostname, ImageId: stringPtrOrNil(pullResult.ImageID), diff --git a/internal/api/container/container_pull_public_test.go b/internal/api/docker/container_pull_public_test.go similarity index 78% rename from internal/api/container/container_pull_public_test.go rename to internal/api/docker/container_pull_public_test.go index 698385b5..0293017f 100644 --- a/internal/api/container/container_pull_public_test.go +++ b/internal/api/docker/container_pull_public_test.go @@ -35,8 +35,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -76,24 +76,24 @@ func (s *ContainerPullPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPull() { +func (s *ContainerPullPublicTestSuite) TestPostNodeContainerDockerPull() { tests := []struct { name string - request gen.PostNodeContainerPullRequestObject + request gen.PostNodeContainerDockerPullRequestObject setupMock func() - validateFunc func(resp gen.PostNodeContainerPullResponseObject) + validateFunc func(resp gen.PostNodeContainerDockerPullResponseObject) }{ { name: "success", - request: gen.PostNodeContainerPullRequestObject{ + request: gen.PostNodeContainerDockerPullRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerPullJSONRequestBody{ + Body: &gen.PostNodeContainerDockerPullJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerPull( + ModifyDockerPull( gomock.Any(), "server1", gomock.Any(), @@ -107,8 +107,8 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPull() { ), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerPullResponseObject) { - r, ok := resp.(gen.PostNodeContainerPull202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerPullResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerPull202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -124,15 +124,15 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPull() { }, { name: "validation error empty hostname", - request: gen.PostNodeContainerPullRequestObject{ + request: gen.PostNodeContainerDockerPullRequestObject{ Hostname: "", - Body: &gen.PostNodeContainerPullJSONRequestBody{ + Body: &gen.PostNodeContainerDockerPullJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerPullResponseObject) { - r, ok := resp.(gen.PostNodeContainerPull400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerPullResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerPull400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -140,38 +140,38 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPull() { }, { name: "body validation error empty image", - request: gen.PostNodeContainerPullRequestObject{ + request: gen.PostNodeContainerDockerPullRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerPullJSONRequestBody{ + Body: &gen.PostNodeContainerDockerPullJSONRequestBody{ Image: "", }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerPullResponseObject) { - r, ok := resp.(gen.PostNodeContainerPull400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerPullResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerPull400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) }, }, { name: "job client error", - request: gen.PostNodeContainerPullRequestObject{ + request: gen.PostNodeContainerDockerPullRequestObject{ Hostname: "server1", - Body: &gen.PostNodeContainerPullJSONRequestBody{ + Body: &gen.PostNodeContainerDockerPullJSONRequestBody{ Image: "nginx:latest", }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerPull( + ModifyDockerPull( gomock.Any(), "server1", gomock.Any(), ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.PostNodeContainerPullResponseObject) { - _, ok := resp.(gen.PostNodeContainerPull500JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerPullResponseObject) { + _, ok := resp.(gen.PostNodeContainerDockerPull500JSONResponse) s.True(ok) }, }, @@ -181,14 +181,14 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPull() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.PostNodeContainerPull(s.ctx, tt.request) + resp, err := s.handler.PostNodeContainerDockerPull(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPullValidationHTTP() { +func (s *ContainerPullPublicTestSuite) TestPostNodeContainerDockerPullValidationHTTP() { tests := []struct { name string path string @@ -199,12 +199,12 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPullValidationHTTP() }{ { name: "when valid request", - path: "/node/server1/container/pull", + path: "/node/server1/container/docker/pull", body: `{"image":"nginx:latest"}`, setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerPull(gomock.Any(), "server1", gomock.Any()). + ModifyDockerPull(gomock.Any(), "server1", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -220,7 +220,7 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPullValidationHTTP() }, { name: "when missing image", - path: "/node/server1/container/pull", + path: "/node/server1/container/docker/pull", body: `{}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) @@ -230,7 +230,7 @@ func (s *ContainerPullPublicTestSuite) TestPostNodeContainerPullValidationHTTP() }, { name: "when target agent not found", - path: "/node/nonexistent/container/pull", + path: "/node/nonexistent/container/docker/pull", body: `{"image":"nginx:latest"}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) diff --git a/internal/api/container/container_remove.go b/internal/api/docker/container_remove.go similarity index 73% rename from internal/api/container/container_remove.go rename to internal/api/docker/container_remove.go index 7848f630..e141d924 100644 --- a/internal/api/container/container_remove.go +++ b/internal/api/docker/container_remove.go @@ -26,23 +26,23 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" ) -// DeleteNodeContainerByID removes a container from a target node. -func (s *Container) DeleteNodeContainerByID( +// DeleteNodeContainerDockerByID removes a container from a target node. +func (s *Container) DeleteNodeContainerDockerByID( ctx context.Context, - request gen.DeleteNodeContainerByIDRequestObject, -) (gen.DeleteNodeContainerByIDResponseObject, error) { + request gen.DeleteNodeContainerDockerByIDRequestObject, +) (gen.DeleteNodeContainerDockerByIDResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.DeleteNodeContainerByID400JSONResponse{Error: &errMsg}, nil + return gen.DeleteNodeContainerDockerByID400JSONResponse{Error: &errMsg}, nil } hostname := request.Hostname id := request.Id - data := &job.ContainerRemoveData{} + data := &job.DockerRemoveData{} if request.Params.Force != nil { data.Force = *request.Params.Force } @@ -53,19 +53,19 @@ func (s *Container) DeleteNodeContainerByID( slog.Bool("force", data.Force), ) - resp, err := s.JobClient.ModifyContainerRemove(ctx, hostname, id, data) + resp, err := s.JobClient.ModifyDockerRemove(ctx, hostname, id, data) if err != nil { errMsg := err.Error() - return gen.DeleteNodeContainerByID500JSONResponse{Error: &errMsg}, nil + return gen.DeleteNodeContainerDockerByID500JSONResponse{Error: &errMsg}, nil } jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed msg := "container removed" - return gen.DeleteNodeContainerByID202JSONResponse{ + return gen.DeleteNodeContainerDockerByID202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerActionResultItem{ + Results: []gen.DockerActionResultItem{ { Hostname: resp.Hostname, Id: &id, diff --git a/internal/api/container/container_remove_public_test.go b/internal/api/docker/container_remove_public_test.go similarity index 78% rename from internal/api/container/container_remove_public_test.go rename to internal/api/docker/container_remove_public_test.go index d52eade4..490ca6df 100644 --- a/internal/api/container/container_remove_public_test.go +++ b/internal/api/docker/container_remove_public_test.go @@ -33,8 +33,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -74,23 +74,23 @@ func (s *ContainerRemovePublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { +func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerDockerByID() { tests := []struct { name string - request gen.DeleteNodeContainerByIDRequestObject + request gen.DeleteNodeContainerDockerByIDRequestObject setupMock func() - validateFunc func(resp gen.DeleteNodeContainerByIDResponseObject) + validateFunc func(resp gen.DeleteNodeContainerDockerByIDResponseObject) }{ { name: "success", - request: gen.DeleteNodeContainerByIDRequestObject{ + request: gen.DeleteNodeContainerDockerByIDRequestObject{ Hostname: "server1", Id: "abc123", - Params: gen.DeleteNodeContainerByIDParams{}, + Params: gen.DeleteNodeContainerDockerByIDParams{}, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerRemove( + ModifyDockerRemove( gomock.Any(), "server1", "abc123", @@ -102,8 +102,8 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { Changed: boolPtr(true), }, nil) }, - validateFunc: func(resp gen.DeleteNodeContainerByIDResponseObject) { - r, ok := resp.(gen.DeleteNodeContainerByID202JSONResponse) + validateFunc: func(resp gen.DeleteNodeContainerDockerByIDResponseObject) { + r, ok := resp.(gen.DeleteNodeContainerDockerByID202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -117,14 +117,14 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { }, { name: "success with force", - request: gen.DeleteNodeContainerByIDRequestObject{ + request: gen.DeleteNodeContainerDockerByIDRequestObject{ Hostname: "server1", Id: "abc123", - Params: gen.DeleteNodeContainerByIDParams{Force: boolPtr(true)}, + Params: gen.DeleteNodeContainerDockerByIDParams{Force: boolPtr(true)}, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerRemove( + ModifyDockerRemove( gomock.Any(), "server1", "abc123", @@ -136,22 +136,22 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { Changed: boolPtr(true), }, nil) }, - validateFunc: func(resp gen.DeleteNodeContainerByIDResponseObject) { - r, ok := resp.(gen.DeleteNodeContainerByID202JSONResponse) + validateFunc: func(resp gen.DeleteNodeContainerDockerByIDResponseObject) { + r, ok := resp.(gen.DeleteNodeContainerDockerByID202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) }, }, { name: "validation error empty hostname", - request: gen.DeleteNodeContainerByIDRequestObject{ + request: gen.DeleteNodeContainerDockerByIDRequestObject{ Hostname: "", Id: "abc123", - Params: gen.DeleteNodeContainerByIDParams{}, + Params: gen.DeleteNodeContainerDockerByIDParams{}, }, setupMock: func() {}, - validateFunc: func(resp gen.DeleteNodeContainerByIDResponseObject) { - r, ok := resp.(gen.DeleteNodeContainerByID400JSONResponse) + validateFunc: func(resp gen.DeleteNodeContainerDockerByIDResponseObject) { + r, ok := resp.(gen.DeleteNodeContainerDockerByID400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -159,14 +159,14 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { }, { name: "job client error", - request: gen.DeleteNodeContainerByIDRequestObject{ + request: gen.DeleteNodeContainerDockerByIDRequestObject{ Hostname: "server1", Id: "abc123", - Params: gen.DeleteNodeContainerByIDParams{}, + Params: gen.DeleteNodeContainerDockerByIDParams{}, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerRemove( + ModifyDockerRemove( gomock.Any(), "server1", "abc123", @@ -174,8 +174,8 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.DeleteNodeContainerByIDResponseObject) { - _, ok := resp.(gen.DeleteNodeContainerByID500JSONResponse) + validateFunc: func(resp gen.DeleteNodeContainerDockerByIDResponseObject) { + _, ok := resp.(gen.DeleteNodeContainerDockerByID500JSONResponse) s.True(ok) }, }, @@ -185,14 +185,14 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByID() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.DeleteNodeContainerByID(s.ctx, tt.request) + resp, err := s.handler.DeleteNodeContainerDockerByID(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByIDValidationHTTP() { +func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerDockerByIDValidationHTTP() { tests := []struct { name string path string @@ -202,11 +202,11 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByIDValidationHT }{ { name: "when valid request", - path: "/node/server1/container/abc123", + path: "/node/server1/container/docker/abc123", setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerRemove(gomock.Any(), "server1", "abc123", gomock.Any()). + ModifyDockerRemove(gomock.Any(), "server1", "abc123", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -219,7 +219,7 @@ func (s *ContainerRemovePublicTestSuite) TestDeleteNodeContainerByIDValidationHT }, { name: "when target agent not found", - path: "/node/nonexistent/container/abc123", + path: "/node/nonexistent/container/docker/abc123", setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) }, diff --git a/internal/api/container/container_start.go b/internal/api/docker/container_start.go similarity index 73% rename from internal/api/container/container_start.go rename to internal/api/docker/container_start.go index 5d010b92..b2c50a84 100644 --- a/internal/api/container/container_start.go +++ b/internal/api/docker/container_start.go @@ -26,16 +26,16 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" ) -// PostNodeContainerStart starts a container on a target node. -func (s *Container) PostNodeContainerStart( +// PostNodeContainerDockerStart starts a container on a target node. +func (s *Container) PostNodeContainerDockerStart( ctx context.Context, - request gen.PostNodeContainerStartRequestObject, -) (gen.PostNodeContainerStartResponseObject, error) { + request gen.PostNodeContainerDockerStartRequestObject, +) (gen.PostNodeContainerDockerStartResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.PostNodeContainerStart400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerStart400JSONResponse{Error: &errMsg}, nil } hostname := request.Hostname @@ -46,19 +46,19 @@ func (s *Container) PostNodeContainerStart( slog.String("id", id), ) - resp, err := s.JobClient.ModifyContainerStart(ctx, hostname, id) + resp, err := s.JobClient.ModifyDockerStart(ctx, hostname, id) if err != nil { errMsg := err.Error() - return gen.PostNodeContainerStart500JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerStart500JSONResponse{Error: &errMsg}, nil } jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed msg := "container started" - return gen.PostNodeContainerStart202JSONResponse{ + return gen.PostNodeContainerDockerStart202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerActionResultItem{ + Results: []gen.DockerActionResultItem{ { Hostname: resp.Hostname, Id: &id, diff --git a/internal/api/container/container_start_public_test.go b/internal/api/docker/container_start_public_test.go similarity index 80% rename from internal/api/container/container_start_public_test.go rename to internal/api/docker/container_start_public_test.go index 5cabeb55..b48bc9c7 100644 --- a/internal/api/container/container_start_public_test.go +++ b/internal/api/docker/container_start_public_test.go @@ -33,8 +33,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -74,22 +74,22 @@ func (s *ContainerStartPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStart() { +func (s *ContainerStartPublicTestSuite) TestPostNodeContainerDockerStart() { tests := []struct { name string - request gen.PostNodeContainerStartRequestObject + request gen.PostNodeContainerDockerStartRequestObject setupMock func() - validateFunc func(resp gen.PostNodeContainerStartResponseObject) + validateFunc func(resp gen.PostNodeContainerDockerStartResponseObject) }{ { name: "success", - request: gen.PostNodeContainerStartRequestObject{ + request: gen.PostNodeContainerDockerStartRequestObject{ Hostname: "server1", Id: "abc123", }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerStart( + ModifyDockerStart( gomock.Any(), "server1", "abc123", @@ -100,8 +100,8 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStart() { Changed: boolPtr(true), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerStartResponseObject) { - r, ok := resp.(gen.PostNodeContainerStart202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStartResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStart202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -115,13 +115,13 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStart() { }, { name: "validation error empty hostname", - request: gen.PostNodeContainerStartRequestObject{ + request: gen.PostNodeContainerDockerStartRequestObject{ Hostname: "", Id: "abc123", }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerStartResponseObject) { - r, ok := resp.(gen.PostNodeContainerStart400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStartResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStart400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -129,21 +129,21 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStart() { }, { name: "job client error", - request: gen.PostNodeContainerStartRequestObject{ + request: gen.PostNodeContainerDockerStartRequestObject{ Hostname: "server1", Id: "abc123", }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerStart( + ModifyDockerStart( gomock.Any(), "server1", "abc123", ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.PostNodeContainerStartResponseObject) { - _, ok := resp.(gen.PostNodeContainerStart500JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStartResponseObject) { + _, ok := resp.(gen.PostNodeContainerDockerStart500JSONResponse) s.True(ok) }, }, @@ -153,14 +153,14 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStart() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.PostNodeContainerStart(s.ctx, tt.request) + resp, err := s.handler.PostNodeContainerDockerStart(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStartValidationHTTP() { +func (s *ContainerStartPublicTestSuite) TestPostNodeContainerDockerStartValidationHTTP() { tests := []struct { name string path string @@ -170,11 +170,11 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStartValidationHTTP }{ { name: "when valid request", - path: "/node/server1/container/abc123/start", + path: "/node/server1/container/docker/abc123/start", setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerStart(gomock.Any(), "server1", "abc123"). + ModifyDockerStart(gomock.Any(), "server1", "abc123"). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -187,7 +187,7 @@ func (s *ContainerStartPublicTestSuite) TestPostNodeContainerStartValidationHTTP }, { name: "when target agent not found", - path: "/node/nonexistent/container/abc123/start", + path: "/node/nonexistent/container/docker/abc123/start", setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) }, diff --git a/internal/api/container/container_stop.go b/internal/api/docker/container_stop.go similarity index 73% rename from internal/api/container/container_stop.go rename to internal/api/docker/container_stop.go index fd653d6c..f454f46e 100644 --- a/internal/api/container/container_stop.go +++ b/internal/api/docker/container_stop.go @@ -26,30 +26,30 @@ import ( "github.com/google/uuid" - "github.com/retr0h/osapi/internal/api/container/gen" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/job" "github.com/retr0h/osapi/internal/validation" ) -// PostNodeContainerStop stops a container on a target node. -func (s *Container) PostNodeContainerStop( +// PostNodeContainerDockerStop stops a container on a target node. +func (s *Container) PostNodeContainerDockerStop( ctx context.Context, - request gen.PostNodeContainerStopRequestObject, -) (gen.PostNodeContainerStopResponseObject, error) { + request gen.PostNodeContainerDockerStopRequestObject, +) (gen.PostNodeContainerDockerStopResponseObject, error) { if errMsg, ok := validateHostname(request.Hostname); !ok { - return gen.PostNodeContainerStop400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerStop400JSONResponse{Error: &errMsg}, nil } if request.Body != nil { if errMsg, ok := validation.Struct(request.Body); !ok { - return gen.PostNodeContainerStop400JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerStop400JSONResponse{Error: &errMsg}, nil } } hostname := request.Hostname id := request.Id - data := &job.ContainerStopData{} + data := &job.DockerStopData{} if request.Body != nil && request.Body.Timeout != nil { data.Timeout = request.Body.Timeout } @@ -59,19 +59,19 @@ func (s *Container) PostNodeContainerStop( slog.String("id", id), ) - resp, err := s.JobClient.ModifyContainerStop(ctx, hostname, id, data) + resp, err := s.JobClient.ModifyDockerStop(ctx, hostname, id, data) if err != nil { errMsg := err.Error() - return gen.PostNodeContainerStop500JSONResponse{Error: &errMsg}, nil + return gen.PostNodeContainerDockerStop500JSONResponse{Error: &errMsg}, nil } jobUUID := uuid.MustParse(resp.JobID) changed := resp.Changed msg := "container stopped" - return gen.PostNodeContainerStop202JSONResponse{ + return gen.PostNodeContainerDockerStop202JSONResponse{ JobId: &jobUUID, - Results: []gen.ContainerActionResultItem{ + Results: []gen.DockerActionResultItem{ { Hostname: resp.Hostname, Id: &id, diff --git a/internal/api/container/container_stop_public_test.go b/internal/api/docker/container_stop_public_test.go similarity index 77% rename from internal/api/container/container_stop_public_test.go rename to internal/api/docker/container_stop_public_test.go index 0b7d6bae..3c2427cf 100644 --- a/internal/api/container/container_stop_public_test.go +++ b/internal/api/docker/container_stop_public_test.go @@ -34,8 +34,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/retr0h/osapi/internal/api" - apicontainer "github.com/retr0h/osapi/internal/api/container" - "github.com/retr0h/osapi/internal/api/container/gen" + apicontainer "github.com/retr0h/osapi/internal/api/docker" + "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/config" "github.com/retr0h/osapi/internal/job" jobmocks "github.com/retr0h/osapi/internal/job/mocks" @@ -75,23 +75,23 @@ func (s *ContainerStopPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { +func (s *ContainerStopPublicTestSuite) TestPostNodeContainerDockerStop() { tests := []struct { name string - request gen.PostNodeContainerStopRequestObject + request gen.PostNodeContainerDockerStopRequestObject setupMock func() - validateFunc func(resp gen.PostNodeContainerStopResponseObject) + validateFunc func(resp gen.PostNodeContainerDockerStopResponseObject) }{ { name: "success without body", - request: gen.PostNodeContainerStopRequestObject{ + request: gen.PostNodeContainerDockerStopRequestObject{ Hostname: "server1", Id: "abc123", Body: nil, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerStop( + ModifyDockerStop( gomock.Any(), "server1", "abc123", @@ -103,8 +103,8 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { Changed: boolPtr(true), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerStopResponseObject) { - r, ok := resp.(gen.PostNodeContainerStop202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStopResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStop202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -118,16 +118,16 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { }, { name: "success with timeout", - request: gen.PostNodeContainerStopRequestObject{ + request: gen.PostNodeContainerDockerStopRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerStopJSONRequestBody{ + Body: &gen.PostNodeContainerDockerStopJSONRequestBody{ Timeout: intPtr(30), }, }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerStop( + ModifyDockerStop( gomock.Any(), "server1", "abc123", @@ -139,8 +139,8 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { Changed: boolPtr(true), }, nil) }, - validateFunc: func(resp gen.PostNodeContainerStopResponseObject) { - r, ok := resp.(gen.PostNodeContainerStop202JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStopResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStop202JSONResponse) s.True(ok) s.Require().Len(r.Results, 1) s.Equal("agent1", r.Results[0].Hostname) @@ -148,16 +148,16 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { }, { name: "body validation error invalid timeout", - request: gen.PostNodeContainerStopRequestObject{ + request: gen.PostNodeContainerDockerStopRequestObject{ Hostname: "server1", Id: "abc123", - Body: &gen.PostNodeContainerStopJSONRequestBody{ + Body: &gen.PostNodeContainerDockerStopJSONRequestBody{ Timeout: intPtr(999), }, }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerStopResponseObject) { - r, ok := resp.(gen.PostNodeContainerStop400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStopResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStop400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "Timeout") @@ -165,13 +165,13 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { }, { name: "validation error empty hostname", - request: gen.PostNodeContainerStopRequestObject{ + request: gen.PostNodeContainerDockerStopRequestObject{ Hostname: "", Id: "abc123", }, setupMock: func() {}, - validateFunc: func(resp gen.PostNodeContainerStopResponseObject) { - r, ok := resp.(gen.PostNodeContainerStop400JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStopResponseObject) { + r, ok := resp.(gen.PostNodeContainerDockerStop400JSONResponse) s.True(ok) s.Require().NotNil(r.Error) s.Contains(*r.Error, "required") @@ -179,13 +179,13 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { }, { name: "job client error", - request: gen.PostNodeContainerStopRequestObject{ + request: gen.PostNodeContainerDockerStopRequestObject{ Hostname: "server1", Id: "abc123", }, setupMock: func() { s.mockJobClient.EXPECT(). - ModifyContainerStop( + ModifyDockerStop( gomock.Any(), "server1", "abc123", @@ -193,8 +193,8 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { ). Return(nil, assert.AnError) }, - validateFunc: func(resp gen.PostNodeContainerStopResponseObject) { - _, ok := resp.(gen.PostNodeContainerStop500JSONResponse) + validateFunc: func(resp gen.PostNodeContainerDockerStopResponseObject) { + _, ok := resp.(gen.PostNodeContainerDockerStop500JSONResponse) s.True(ok) }, }, @@ -204,14 +204,14 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStop() { s.Run(tt.name, func() { tt.setupMock() - resp, err := s.handler.PostNodeContainerStop(s.ctx, tt.request) + resp, err := s.handler.PostNodeContainerDockerStop(s.ctx, tt.request) s.NoError(err) tt.validateFunc(resp) }) } } -func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStopValidationHTTP() { +func (s *ContainerStopPublicTestSuite) TestPostNodeContainerDockerStopValidationHTTP() { tests := []struct { name string path string @@ -222,12 +222,12 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStopValidationHTTP() }{ { name: "when valid request", - path: "/node/server1/container/abc123/stop", + path: "/node/server1/container/docker/abc123/stop", body: `{"timeout":10}`, setupJobMock: func() *jobmocks.MockJobClient { mock := jobmocks.NewMockJobClient(s.mockCtrl) mock.EXPECT(). - ModifyContainerStop(gomock.Any(), "server1", "abc123", gomock.Any()). + ModifyDockerStop(gomock.Any(), "server1", "abc123", gomock.Any()). Return(&job.Response{ JobID: "550e8400-e29b-41d4-a716-446655440000", Hostname: "agent1", @@ -240,7 +240,7 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStopValidationHTTP() }, { name: "when invalid timeout", - path: "/node/server1/container/abc123/stop", + path: "/node/server1/container/docker/abc123/stop", body: `{"timeout":999}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) @@ -250,7 +250,7 @@ func (s *ContainerStopPublicTestSuite) TestPostNodeContainerStopValidationHTTP() }, { name: "when target agent not found", - path: "/node/nonexistent/container/abc123/stop", + path: "/node/nonexistent/container/docker/abc123/stop", body: `{}`, setupJobMock: func() *jobmocks.MockJobClient { return jobmocks.NewMockJobClient(s.mockCtrl) diff --git a/internal/api/container/convert.go b/internal/api/docker/convert.go similarity index 100% rename from internal/api/container/convert.go rename to internal/api/docker/convert.go diff --git a/internal/api/container/convert_test.go b/internal/api/docker/convert_test.go similarity index 100% rename from internal/api/container/convert_test.go rename to internal/api/docker/convert_test.go diff --git a/internal/api/container/gen/api.yaml b/internal/api/docker/gen/api.yaml similarity index 87% rename from internal/api/container/gen/api.yaml rename to internal/api/docker/gen/api.yaml index abecb3cd..130a46b8 100644 --- a/internal/api/container/gen/api.yaml +++ b/internal/api/docker/gen/api.yaml @@ -21,34 +21,34 @@ --- openapi: 3.0.0 info: - title: Container Management API + title: Docker Management API version: 1.0.0 tags: - - name: container_operations - x-displayName: Node/Container - description: Container lifecycle operations on a target node. - - name: container_exec - x-displayName: Node/Container/Exec + - name: docker_operations + x-displayName: Node/Docker + description: Docker lifecycle operations on a target node. + - name: docker_exec + x-displayName: Node/Docker/Exec description: Command execution inside containers on a target node. - - name: container_image - x-displayName: Node/Container/Image - description: Container image operations on a target node. + - name: docker_image + x-displayName: Node/Docker/Image + description: Docker image operations on a target node. paths: - # ── Container lifecycle ───────────────────────────────────── + # ── Docker lifecycle ────────────────────────────────────── - /node/{hostname}/container: + /node/{hostname}/container/docker: post: summary: Create a container description: > Create a new container on the target node. Returns a job ID for tracking the asynchronous operation. tags: - - container_operations - operationId: PostNodeContainer + - docker_operations + operationId: PostNodeContainerDocker security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' requestBody: @@ -57,14 +57,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerCreateRequest' + $ref: '#/components/schemas/DockerCreateRequest' responses: '202': description: Container creation accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerResultCollectionResponse' + $ref: '#/components/schemas/DockerResultCollectionResponse' '400': description: Invalid request payload. content: @@ -95,11 +95,11 @@ paths: description: > List containers on the target node, optionally filtered by state. tags: - - container_operations - operationId: GetNodeContainer + - docker_operations + operationId: GetNodeContainerDocker security: - BearerAuth: - - container:read + - docker:read parameters: - $ref: '#/components/parameters/Hostname' - name: state @@ -134,7 +134,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerListCollectionResponse' + $ref: '#/components/schemas/DockerListCollectionResponse' '400': description: Invalid request parameters. content: @@ -160,27 +160,27 @@ paths: schema: $ref: '../../common/gen/api.yaml#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}: + /node/{hostname}/container/docker/{id}: get: summary: Inspect a container description: > Get detailed information about a specific container. tags: - - container_operations - operationId: GetNodeContainerByID + - docker_operations + operationId: GetNodeContainerDockerByID security: - BearerAuth: - - container:read + - docker:read parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' responses: '200': description: Container detail. content: application/json: schema: - $ref: '#/components/schemas/ContainerDetailCollectionResponse' + $ref: '#/components/schemas/DockerDetailCollectionResponse' '400': description: Invalid request parameters. content: @@ -218,14 +218,14 @@ paths: Remove a container from the target node. Use the force query parameter to remove a running container. tags: - - container_operations - operationId: DeleteNodeContainerByID + - docker_operations + operationId: DeleteNodeContainerDockerByID security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' - name: force in: query required: false @@ -242,7 +242,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -274,27 +274,27 @@ paths: schema: $ref: '../../common/gen/api.yaml#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}/start: + /node/{hostname}/container/docker/{id}/start: post: summary: Start a container description: > Start a stopped container on the target node. tags: - - container_operations - operationId: PostNodeContainerStart + - docker_operations + operationId: PostNodeContainerDockerStart security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' responses: '202': description: Container start accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -326,35 +326,35 @@ paths: schema: $ref: '../../common/gen/api.yaml#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}/stop: + /node/{hostname}/container/docker/{id}/stop: post: summary: Stop a container description: > Stop a running container on the target node. Optionally specify a timeout before the container is killed. tags: - - container_operations - operationId: PostNodeContainerStop + - docker_operations + operationId: PostNodeContainerDockerStop security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' requestBody: description: Optional stop parameters. required: false content: application/json: schema: - $ref: '#/components/schemas/ContainerStopRequest' + $ref: '#/components/schemas/DockerStopRequest' responses: '202': description: Container stop accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -386,36 +386,36 @@ paths: schema: $ref: '../../common/gen/api.yaml#/components/schemas/ErrorResponse' - # ── Container exec ────────────────────────────────────────── + # ── Docker exec ──────────────────────────────────────────── - /node/{hostname}/container/{id}/exec: + /node/{hostname}/container/docker/{id}/exec: post: summary: Execute a command in a container description: > Execute a command inside a running container on the target node. tags: - - container_exec - operationId: PostNodeContainerExec + - docker_exec + operationId: PostNodeContainerDockerExec security: - BearerAuth: - - container:execute + - docker:execute parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' requestBody: description: The command to execute inside the container. required: true content: application/json: schema: - $ref: '#/components/schemas/ContainerExecRequest' + $ref: '#/components/schemas/DockerExecRequest' responses: '202': description: Container exec accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerExecCollectionResponse' + $ref: '#/components/schemas/DockerExecCollectionResponse' '400': description: Invalid request payload. content: @@ -447,19 +447,19 @@ paths: schema: $ref: '../../common/gen/api.yaml#/components/schemas/ErrorResponse' - # ── Container image ───────────────────────────────────────── + # ── Docker image ─────────────────────────────────────────── - /node/{hostname}/container/pull: + /node/{hostname}/container/docker/pull: post: summary: Pull a container image description: > Pull a container image on the target node. tags: - - container_image - operationId: PostNodeContainerPull + - docker_image + operationId: PostNodeContainerDockerPull security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' requestBody: @@ -468,14 +468,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerPullRequest' + $ref: '#/components/schemas/DockerPullRequest' responses: '202': description: Image pull accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerPullCollectionResponse' + $ref: '#/components/schemas/DockerPullCollectionResponse' '400': description: Invalid request payload. content: @@ -521,7 +521,7 @@ components: type: string minLength: 1 - ContainerId: + DockerId: name: id in: path required: true @@ -551,7 +551,7 @@ components: # ── Request schemas ─────────────────────────────────────── - ContainerCreateRequest: + DockerCreateRequest: type: object properties: image: @@ -599,7 +599,7 @@ components: required: - image - ContainerStopRequest: + DockerStopRequest: type: object properties: timeout: @@ -613,7 +613,7 @@ components: x-oapi-codegen-extra-tags: validate: omitempty,min=0,max=300 - ContainerExecRequest: + DockerExecRequest: type: object properties: command: @@ -636,7 +636,7 @@ components: required: - command - ContainerPullRequest: + DockerPullRequest: type: object properties: image: @@ -652,7 +652,7 @@ components: # ── Response schemas ────────────────────────────────────── - ContainerResponse: + DockerResponse: type: object description: Summary information about a container. properties: @@ -688,7 +688,7 @@ components: required: - hostname - ContainerDetailResponse: + DockerDetailResponse: type: object description: Detailed information about a container. properties: @@ -750,7 +750,7 @@ components: required: - hostname - ContainerActionResultItem: + DockerActionResultItem: type: object description: Result of a container lifecycle action. properties: @@ -773,7 +773,7 @@ components: required: - hostname - ContainerExecResultItem: + DockerExecResultItem: type: object description: Result of a command execution inside a container. properties: @@ -798,7 +798,7 @@ components: required: - hostname - ContainerPullResultItem: + DockerPullResultItem: type: object description: Result of an image pull operation. properties: @@ -826,7 +826,7 @@ components: required: - hostname - ContainerListItem: + DockerListItem: type: object description: Container summary for list operations. properties: @@ -837,7 +837,7 @@ components: type: array description: List of containers on this agent. items: - $ref: '#/components/schemas/ContainerSummary' + $ref: '#/components/schemas/DockerSummary' changed: type: boolean description: Whether the operation modified system state. @@ -847,7 +847,7 @@ components: required: - hostname - ContainerSummary: + DockerSummary: type: object description: Brief container summary. properties: @@ -874,7 +874,7 @@ components: # ── Collection responses ────────────────────────────────── - ContainerResultCollectionResponse: + DockerResultCollectionResponse: type: object properties: job_id: @@ -885,11 +885,11 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerResponse' + $ref: '#/components/schemas/DockerResponse' required: - results - ContainerListCollectionResponse: + DockerListCollectionResponse: type: object properties: job_id: @@ -900,11 +900,11 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerListItem' + $ref: '#/components/schemas/DockerListItem' required: - results - ContainerDetailCollectionResponse: + DockerDetailCollectionResponse: type: object properties: job_id: @@ -915,11 +915,11 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerDetailResponse' + $ref: '#/components/schemas/DockerDetailResponse' required: - results - ContainerActionCollectionResponse: + DockerActionCollectionResponse: type: object properties: job_id: @@ -930,11 +930,11 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerActionResultItem' + $ref: '#/components/schemas/DockerActionResultItem' required: - results - ContainerExecCollectionResponse: + DockerExecCollectionResponse: type: object properties: job_id: @@ -945,11 +945,11 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerExecResultItem' + $ref: '#/components/schemas/DockerExecResultItem' required: - results - ContainerPullCollectionResponse: + DockerPullCollectionResponse: type: object properties: job_id: @@ -960,6 +960,6 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerPullResultItem' + $ref: '#/components/schemas/DockerPullResultItem' required: - results diff --git a/internal/api/container/gen/cfg.yaml b/internal/api/docker/gen/cfg.yaml similarity index 98% rename from internal/api/container/gen/cfg.yaml rename to internal/api/docker/gen/cfg.yaml index 392eda5a..042895cb 100644 --- a/internal/api/container/gen/cfg.yaml +++ b/internal/api/docker/gen/cfg.yaml @@ -20,7 +20,7 @@ --- package: gen -output: container.gen.go +output: docker.gen.go generate: models: true echo-server: true diff --git a/internal/api/docker/gen/docker.gen.go b/internal/api/docker/gen/docker.gen.go new file mode 100644 index 00000000..ab0371cd --- /dev/null +++ b/internal/api/docker/gen/docker.gen.go @@ -0,0 +1,1351 @@ +// Package gen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT. +package gen + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/oapi-codegen/runtime" + strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" + openapi_types "github.com/oapi-codegen/runtime/types" + externalRef0 "github.com/retr0h/osapi/internal/api/common/gen" +) + +const ( + BearerAuthScopes = "BearerAuth.Scopes" +) + +// Defines values for GetNodeContainerDockerParamsState. +const ( + All GetNodeContainerDockerParamsState = "all" + Running GetNodeContainerDockerParamsState = "running" + Stopped GetNodeContainerDockerParamsState = "stopped" +) + +// DockerActionCollectionResponse defines model for DockerActionCollectionResponse. +type DockerActionCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerActionResultItem `json:"results"` +} + +// DockerActionResultItem Result of a container lifecycle action. +type DockerActionResultItem struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` + + // Id Container identifier. + Id *string `json:"id,omitempty"` + + // Message Status message. + Message *string `json:"message,omitempty"` +} + +// DockerCreateRequest defines model for DockerCreateRequest. +type DockerCreateRequest struct { + // AutoStart Whether to start the container immediately after creation. Defaults to true. + AutoStart *bool `json:"auto_start,omitempty"` + + // Command Command to run in the container. + Command *[]string `json:"command,omitempty"` + + // Env Environment variables in KEY=VALUE format. + Env *[]string `json:"env,omitempty"` + + // Image Container image reference (e.g., "nginx:latest"). + Image string `json:"image" validate:"required,min=1"` + + // Name Optional name for the container. + Name *string `json:"name,omitempty"` + + // Ports Port mappings in host_port:container_port format. + Ports *[]string `json:"ports,omitempty"` + + // Volumes Volume mounts in host_path:container_path format. + Volumes *[]string `json:"volumes,omitempty"` +} + +// DockerDetailCollectionResponse defines model for DockerDetailCollectionResponse. +type DockerDetailCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerDetailResponse `json:"results"` +} + +// DockerDetailResponse Detailed information about a container. +type DockerDetailResponse struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Created Container creation timestamp. + Created *string `json:"created,omitempty"` + + // Env Environment variables. + Env *[]string `json:"env,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Health Health check status if configured. + Health *string `json:"health,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` + + // Id Container identifier. + Id *string `json:"id,omitempty"` + + // Image Image used by the container. + Image *string `json:"image,omitempty"` + + // Mounts Volume mounts. + Mounts *[]string `json:"mounts,omitempty"` + + // Name Container name. + Name *string `json:"name,omitempty"` + + // NetworkSettings Network configuration. + NetworkSettings *map[string]string `json:"network_settings,omitempty"` + + // Ports Port mappings. + Ports *[]string `json:"ports,omitempty"` + + // State Current container state. + State *string `json:"state,omitempty"` +} + +// DockerExecCollectionResponse defines model for DockerExecCollectionResponse. +type DockerExecCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerExecResultItem `json:"results"` +} + +// DockerExecRequest defines model for DockerExecRequest. +type DockerExecRequest struct { + // Command Command to execute inside the container. + Command []string `json:"command" validate:"required,min=1"` + + // Env Additional environment variables in KEY=VALUE format. + Env *[]string `json:"env,omitempty"` + + // WorkingDir Working directory inside the container. + WorkingDir *string `json:"working_dir,omitempty"` +} + +// DockerExecResultItem Result of a command execution inside a container. +type DockerExecResultItem struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // ExitCode Exit code of the command. + ExitCode *int `json:"exit_code,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` + + // Stderr Standard error output of the command. + Stderr *string `json:"stderr,omitempty"` + + // Stdout Standard output of the command. + Stdout *string `json:"stdout,omitempty"` +} + +// DockerListCollectionResponse defines model for DockerListCollectionResponse. +type DockerListCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerListItem `json:"results"` +} + +// DockerListItem Container summary for list operations. +type DockerListItem struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Containers List of containers on this agent. + Containers *[]DockerSummary `json:"containers,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` +} + +// DockerPullCollectionResponse defines model for DockerPullCollectionResponse. +type DockerPullCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerPullResultItem `json:"results"` +} + +// DockerPullRequest defines model for DockerPullRequest. +type DockerPullRequest struct { + // Image Image reference to pull (e.g., "nginx:latest", "docker.io/library/alpine:3.18"). + Image string `json:"image" validate:"required,min=1"` +} + +// DockerPullResultItem Result of an image pull operation. +type DockerPullResultItem struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` + + // ImageId The pulled image ID. + ImageId *string `json:"image_id,omitempty"` + + // Size Image size in bytes. + Size *int64 `json:"size,omitempty"` + + // Tag The image tag that was pulled. + Tag *string `json:"tag,omitempty"` +} + +// DockerResponse Summary information about a container. +type DockerResponse struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Created Container creation timestamp. + Created *string `json:"created,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` + + // Id Container identifier. + Id *string `json:"id,omitempty"` + + // Image Image used by the container. + Image *string `json:"image,omitempty"` + + // Name Container name. + Name *string `json:"name,omitempty"` + + // State Current container state. + State *string `json:"state,omitempty"` +} + +// DockerResultCollectionResponse defines model for DockerResultCollectionResponse. +type DockerResultCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerResponse `json:"results"` +} + +// DockerStopRequest defines model for DockerStopRequest. +type DockerStopRequest struct { + // Timeout Seconds to wait before killing the container. Defaults to 10. + Timeout *int `json:"timeout,omitempty" validate:"omitempty,min=0,max=300"` +} + +// DockerSummary Brief container summary. +type DockerSummary struct { + // Created Container creation timestamp. + Created *string `json:"created,omitempty"` + + // Id Container identifier. + Id *string `json:"id,omitempty"` + + // Image Image used by the container. + Image *string `json:"image,omitempty"` + + // Name Container name. + Name *string `json:"name,omitempty"` + + // State Current container state. + State *string `json:"state,omitempty"` +} + +// ErrorResponse defines model for ErrorResponse. +type ErrorResponse = externalRef0.ErrorResponse + +// DockerId defines model for DockerId. +type DockerId = string + +// Hostname defines model for Hostname. +type Hostname = string + +// GetNodeContainerDockerParams defines parameters for GetNodeContainerDocker. +type GetNodeContainerDockerParams struct { + // State Filter containers by state. Defaults to "all". + State *GetNodeContainerDockerParamsState `form:"state,omitempty" json:"state,omitempty" validate:"omitempty,oneof=running stopped all"` + + // Limit Maximum number of containers to return. + Limit *int `form:"limit,omitempty" json:"limit,omitempty" validate:"omitempty,min=1,max=100"` +} + +// GetNodeContainerDockerParamsState defines parameters for GetNodeContainerDocker. +type GetNodeContainerDockerParamsState string + +// DeleteNodeContainerDockerByIDParams defines parameters for DeleteNodeContainerDockerByID. +type DeleteNodeContainerDockerByIDParams struct { + // Force Force removal of a running container. + Force *bool `form:"force,omitempty" json:"force,omitempty" validate:"omitempty"` +} + +// PostNodeContainerDockerJSONRequestBody defines body for PostNodeContainerDocker for application/json ContentType. +type PostNodeContainerDockerJSONRequestBody = DockerCreateRequest + +// PostNodeContainerDockerPullJSONRequestBody defines body for PostNodeContainerDockerPull for application/json ContentType. +type PostNodeContainerDockerPullJSONRequestBody = DockerPullRequest + +// PostNodeContainerDockerExecJSONRequestBody defines body for PostNodeContainerDockerExec for application/json ContentType. +type PostNodeContainerDockerExecJSONRequestBody = DockerExecRequest + +// PostNodeContainerDockerStopJSONRequestBody defines body for PostNodeContainerDockerStop for application/json ContentType. +type PostNodeContainerDockerStopJSONRequestBody = DockerStopRequest + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // List containers + // (GET /node/{hostname}/container/docker) + GetNodeContainerDocker(ctx echo.Context, hostname Hostname, params GetNodeContainerDockerParams) error + // Create a container + // (POST /node/{hostname}/container/docker) + PostNodeContainerDocker(ctx echo.Context, hostname Hostname) error + // Pull a container image + // (POST /node/{hostname}/container/docker/pull) + PostNodeContainerDockerPull(ctx echo.Context, hostname Hostname) error + // Remove a container + // (DELETE /node/{hostname}/container/docker/{id}) + DeleteNodeContainerDockerByID(ctx echo.Context, hostname Hostname, id DockerId, params DeleteNodeContainerDockerByIDParams) error + // Inspect a container + // (GET /node/{hostname}/container/docker/{id}) + GetNodeContainerDockerByID(ctx echo.Context, hostname Hostname, id DockerId) error + // Execute a command in a container + // (POST /node/{hostname}/container/docker/{id}/exec) + PostNodeContainerDockerExec(ctx echo.Context, hostname Hostname, id DockerId) error + // Start a container + // (POST /node/{hostname}/container/docker/{id}/start) + PostNodeContainerDockerStart(ctx echo.Context, hostname Hostname, id DockerId) error + // Stop a container + // (POST /node/{hostname}/container/docker/{id}/stop) + PostNodeContainerDockerStop(ctx echo.Context, hostname Hostname, id DockerId) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// GetNodeContainerDocker converts echo context to params. +func (w *ServerInterfaceWrapper) GetNodeContainerDocker(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:read"}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetNodeContainerDockerParams + // ------------- Optional query parameter "state" ------------- + + err = runtime.BindQueryParameter("form", true, false, "state", ctx.QueryParams(), ¶ms.State) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter state: %s", err)) + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetNodeContainerDocker(ctx, hostname, params) + return err +} + +// PostNodeContainerDocker converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeContainerDocker(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:write"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeContainerDocker(ctx, hostname) + return err +} + +// PostNodeContainerDockerPull converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeContainerDockerPull(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:write"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeContainerDockerPull(ctx, hostname) + return err +} + +// DeleteNodeContainerDockerByID converts echo context to params. +func (w *ServerInterfaceWrapper) DeleteNodeContainerDockerByID(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + // ------------- Path parameter "id" ------------- + var id DockerId + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:write"}) + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteNodeContainerDockerByIDParams + // ------------- Optional query parameter "force" ------------- + + err = runtime.BindQueryParameter("form", true, false, "force", ctx.QueryParams(), ¶ms.Force) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter force: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.DeleteNodeContainerDockerByID(ctx, hostname, id, params) + return err +} + +// GetNodeContainerDockerByID converts echo context to params. +func (w *ServerInterfaceWrapper) GetNodeContainerDockerByID(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + // ------------- Path parameter "id" ------------- + var id DockerId + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:read"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetNodeContainerDockerByID(ctx, hostname, id) + return err +} + +// PostNodeContainerDockerExec converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeContainerDockerExec(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + // ------------- Path parameter "id" ------------- + var id DockerId + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:execute"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeContainerDockerExec(ctx, hostname, id) + return err +} + +// PostNodeContainerDockerStart converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeContainerDockerStart(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + // ------------- Path parameter "id" ------------- + var id DockerId + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:write"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeContainerDockerStart(ctx, hostname, id) + return err +} + +// PostNodeContainerDockerStop converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeContainerDockerStop(ctx echo.Context) error { + var err error + // ------------- Path parameter "hostname" ------------- + var hostname Hostname + + err = runtime.BindStyledParameterWithOptions("simple", "hostname", ctx.Param("hostname"), &hostname, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hostname: %s", err)) + } + + // ------------- Path parameter "id" ------------- + var id DockerId + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{"docker:write"}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeContainerDockerStop(ctx, hostname, id) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/node/:hostname/container/docker", wrapper.GetNodeContainerDocker) + router.POST(baseURL+"/node/:hostname/container/docker", wrapper.PostNodeContainerDocker) + router.POST(baseURL+"/node/:hostname/container/docker/pull", wrapper.PostNodeContainerDockerPull) + router.DELETE(baseURL+"/node/:hostname/container/docker/:id", wrapper.DeleteNodeContainerDockerByID) + router.GET(baseURL+"/node/:hostname/container/docker/:id", wrapper.GetNodeContainerDockerByID) + router.POST(baseURL+"/node/:hostname/container/docker/:id/exec", wrapper.PostNodeContainerDockerExec) + router.POST(baseURL+"/node/:hostname/container/docker/:id/start", wrapper.PostNodeContainerDockerStart) + router.POST(baseURL+"/node/:hostname/container/docker/:id/stop", wrapper.PostNodeContainerDockerStop) + +} + +type GetNodeContainerDockerRequestObject struct { + Hostname Hostname `json:"hostname"` + Params GetNodeContainerDockerParams +} + +type GetNodeContainerDockerResponseObject interface { + VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error +} + +type GetNodeContainerDocker200JSONResponse DockerListCollectionResponse + +func (response GetNodeContainerDocker200JSONResponse) VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDocker400JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDocker400JSONResponse) VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDocker401JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDocker401JSONResponse) VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDocker403JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDocker403JSONResponse) VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDocker500JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDocker500JSONResponse) VisitGetNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerRequestObject struct { + Hostname Hostname `json:"hostname"` + Body *PostNodeContainerDockerJSONRequestBody +} + +type PostNodeContainerDockerResponseObject interface { + VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error +} + +type PostNodeContainerDocker202JSONResponse DockerResultCollectionResponse + +func (response PostNodeContainerDocker202JSONResponse) VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDocker400JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDocker400JSONResponse) VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDocker401JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDocker401JSONResponse) VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDocker403JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDocker403JSONResponse) VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDocker500JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDocker500JSONResponse) VisitPostNodeContainerDockerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerPullRequestObject struct { + Hostname Hostname `json:"hostname"` + Body *PostNodeContainerDockerPullJSONRequestBody +} + +type PostNodeContainerDockerPullResponseObject interface { + VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error +} + +type PostNodeContainerDockerPull202JSONResponse DockerPullCollectionResponse + +func (response PostNodeContainerDockerPull202JSONResponse) VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerPull400JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerPull400JSONResponse) VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerPull401JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerPull401JSONResponse) VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerPull403JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerPull403JSONResponse) VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerPull500JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerPull500JSONResponse) VisitPostNodeContainerDockerPullResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByIDRequestObject struct { + Hostname Hostname `json:"hostname"` + Id DockerId `json:"id"` + Params DeleteNodeContainerDockerByIDParams +} + +type DeleteNodeContainerDockerByIDResponseObject interface { + VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error +} + +type DeleteNodeContainerDockerByID202JSONResponse DockerActionCollectionResponse + +func (response DeleteNodeContainerDockerByID202JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByID400JSONResponse externalRef0.ErrorResponse + +func (response DeleteNodeContainerDockerByID400JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByID401JSONResponse externalRef0.ErrorResponse + +func (response DeleteNodeContainerDockerByID401JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByID403JSONResponse externalRef0.ErrorResponse + +func (response DeleteNodeContainerDockerByID403JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByID404JSONResponse externalRef0.ErrorResponse + +func (response DeleteNodeContainerDockerByID404JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteNodeContainerDockerByID500JSONResponse externalRef0.ErrorResponse + +func (response DeleteNodeContainerDockerByID500JSONResponse) VisitDeleteNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByIDRequestObject struct { + Hostname Hostname `json:"hostname"` + Id DockerId `json:"id"` +} + +type GetNodeContainerDockerByIDResponseObject interface { + VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error +} + +type GetNodeContainerDockerByID200JSONResponse DockerDetailCollectionResponse + +func (response GetNodeContainerDockerByID200JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByID400JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDockerByID400JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByID401JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDockerByID401JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByID403JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDockerByID403JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByID404JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDockerByID404JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetNodeContainerDockerByID500JSONResponse externalRef0.ErrorResponse + +func (response GetNodeContainerDockerByID500JSONResponse) VisitGetNodeContainerDockerByIDResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExecRequestObject struct { + Hostname Hostname `json:"hostname"` + Id DockerId `json:"id"` + Body *PostNodeContainerDockerExecJSONRequestBody +} + +type PostNodeContainerDockerExecResponseObject interface { + VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error +} + +type PostNodeContainerDockerExec202JSONResponse DockerExecCollectionResponse + +func (response PostNodeContainerDockerExec202JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExec400JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerExec400JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExec401JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerExec401JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExec403JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerExec403JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExec404JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerExec404JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerExec500JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerExec500JSONResponse) VisitPostNodeContainerDockerExecResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStartRequestObject struct { + Hostname Hostname `json:"hostname"` + Id DockerId `json:"id"` +} + +type PostNodeContainerDockerStartResponseObject interface { + VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error +} + +type PostNodeContainerDockerStart202JSONResponse DockerActionCollectionResponse + +func (response PostNodeContainerDockerStart202JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStart400JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStart400JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStart401JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStart401JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStart403JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStart403JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStart404JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStart404JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStart500JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStart500JSONResponse) VisitPostNodeContainerDockerStartResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStopRequestObject struct { + Hostname Hostname `json:"hostname"` + Id DockerId `json:"id"` + Body *PostNodeContainerDockerStopJSONRequestBody +} + +type PostNodeContainerDockerStopResponseObject interface { + VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error +} + +type PostNodeContainerDockerStop202JSONResponse DockerActionCollectionResponse + +func (response PostNodeContainerDockerStop202JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(202) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStop400JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStop400JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStop401JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStop401JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStop403JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStop403JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStop404JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStop404JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type PostNodeContainerDockerStop500JSONResponse externalRef0.ErrorResponse + +func (response PostNodeContainerDockerStop500JSONResponse) VisitPostNodeContainerDockerStopResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // List containers + // (GET /node/{hostname}/container/docker) + GetNodeContainerDocker(ctx context.Context, request GetNodeContainerDockerRequestObject) (GetNodeContainerDockerResponseObject, error) + // Create a container + // (POST /node/{hostname}/container/docker) + PostNodeContainerDocker(ctx context.Context, request PostNodeContainerDockerRequestObject) (PostNodeContainerDockerResponseObject, error) + // Pull a container image + // (POST /node/{hostname}/container/docker/pull) + PostNodeContainerDockerPull(ctx context.Context, request PostNodeContainerDockerPullRequestObject) (PostNodeContainerDockerPullResponseObject, error) + // Remove a container + // (DELETE /node/{hostname}/container/docker/{id}) + DeleteNodeContainerDockerByID(ctx context.Context, request DeleteNodeContainerDockerByIDRequestObject) (DeleteNodeContainerDockerByIDResponseObject, error) + // Inspect a container + // (GET /node/{hostname}/container/docker/{id}) + GetNodeContainerDockerByID(ctx context.Context, request GetNodeContainerDockerByIDRequestObject) (GetNodeContainerDockerByIDResponseObject, error) + // Execute a command in a container + // (POST /node/{hostname}/container/docker/{id}/exec) + PostNodeContainerDockerExec(ctx context.Context, request PostNodeContainerDockerExecRequestObject) (PostNodeContainerDockerExecResponseObject, error) + // Start a container + // (POST /node/{hostname}/container/docker/{id}/start) + PostNodeContainerDockerStart(ctx context.Context, request PostNodeContainerDockerStartRequestObject) (PostNodeContainerDockerStartResponseObject, error) + // Stop a container + // (POST /node/{hostname}/container/docker/{id}/stop) + PostNodeContainerDockerStop(ctx context.Context, request PostNodeContainerDockerStopRequestObject) (PostNodeContainerDockerStopResponseObject, error) +} + +type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc +type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// GetNodeContainerDocker operation middleware +func (sh *strictHandler) GetNodeContainerDocker(ctx echo.Context, hostname Hostname, params GetNodeContainerDockerParams) error { + var request GetNodeContainerDockerRequestObject + + request.Hostname = hostname + request.Params = params + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetNodeContainerDocker(ctx.Request().Context(), request.(GetNodeContainerDockerRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetNodeContainerDocker") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(GetNodeContainerDockerResponseObject); ok { + return validResponse.VisitGetNodeContainerDockerResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostNodeContainerDocker operation middleware +func (sh *strictHandler) PostNodeContainerDocker(ctx echo.Context, hostname Hostname) error { + var request PostNodeContainerDockerRequestObject + + request.Hostname = hostname + + var body PostNodeContainerDockerJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostNodeContainerDocker(ctx.Request().Context(), request.(PostNodeContainerDockerRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostNodeContainerDocker") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostNodeContainerDockerResponseObject); ok { + return validResponse.VisitPostNodeContainerDockerResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostNodeContainerDockerPull operation middleware +func (sh *strictHandler) PostNodeContainerDockerPull(ctx echo.Context, hostname Hostname) error { + var request PostNodeContainerDockerPullRequestObject + + request.Hostname = hostname + + var body PostNodeContainerDockerPullJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostNodeContainerDockerPull(ctx.Request().Context(), request.(PostNodeContainerDockerPullRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostNodeContainerDockerPull") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostNodeContainerDockerPullResponseObject); ok { + return validResponse.VisitPostNodeContainerDockerPullResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// DeleteNodeContainerDockerByID operation middleware +func (sh *strictHandler) DeleteNodeContainerDockerByID(ctx echo.Context, hostname Hostname, id DockerId, params DeleteNodeContainerDockerByIDParams) error { + var request DeleteNodeContainerDockerByIDRequestObject + + request.Hostname = hostname + request.Id = id + request.Params = params + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteNodeContainerDockerByID(ctx.Request().Context(), request.(DeleteNodeContainerDockerByIDRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteNodeContainerDockerByID") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(DeleteNodeContainerDockerByIDResponseObject); ok { + return validResponse.VisitDeleteNodeContainerDockerByIDResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// GetNodeContainerDockerByID operation middleware +func (sh *strictHandler) GetNodeContainerDockerByID(ctx echo.Context, hostname Hostname, id DockerId) error { + var request GetNodeContainerDockerByIDRequestObject + + request.Hostname = hostname + request.Id = id + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetNodeContainerDockerByID(ctx.Request().Context(), request.(GetNodeContainerDockerByIDRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetNodeContainerDockerByID") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(GetNodeContainerDockerByIDResponseObject); ok { + return validResponse.VisitGetNodeContainerDockerByIDResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostNodeContainerDockerExec operation middleware +func (sh *strictHandler) PostNodeContainerDockerExec(ctx echo.Context, hostname Hostname, id DockerId) error { + var request PostNodeContainerDockerExecRequestObject + + request.Hostname = hostname + request.Id = id + + var body PostNodeContainerDockerExecJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostNodeContainerDockerExec(ctx.Request().Context(), request.(PostNodeContainerDockerExecRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostNodeContainerDockerExec") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostNodeContainerDockerExecResponseObject); ok { + return validResponse.VisitPostNodeContainerDockerExecResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostNodeContainerDockerStart operation middleware +func (sh *strictHandler) PostNodeContainerDockerStart(ctx echo.Context, hostname Hostname, id DockerId) error { + var request PostNodeContainerDockerStartRequestObject + + request.Hostname = hostname + request.Id = id + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostNodeContainerDockerStart(ctx.Request().Context(), request.(PostNodeContainerDockerStartRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostNodeContainerDockerStart") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostNodeContainerDockerStartResponseObject); ok { + return validResponse.VisitPostNodeContainerDockerStartResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostNodeContainerDockerStop operation middleware +func (sh *strictHandler) PostNodeContainerDockerStop(ctx echo.Context, hostname Hostname, id DockerId) error { + var request PostNodeContainerDockerStopRequestObject + + request.Hostname = hostname + request.Id = id + + var body PostNodeContainerDockerStopJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostNodeContainerDockerStop(ctx.Request().Context(), request.(PostNodeContainerDockerStopRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostNodeContainerDockerStop") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostNodeContainerDockerStopResponseObject); ok { + return validResponse.VisitPostNodeContainerDockerStopResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} diff --git a/internal/api/container/gen/generate.go b/internal/api/docker/gen/generate.go similarity index 95% rename from internal/api/container/gen/generate.go rename to internal/api/docker/gen/generate.go index d8d19308..9d1f7ec9 100644 --- a/internal/api/container/gen/generate.go +++ b/internal/api/docker/gen/generate.go @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// Package gen contains generated code for the container API. +// Package gen contains generated code for the docker API. package gen //go:generate go tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml diff --git a/internal/api/container/test_ptr_public_test.go b/internal/api/docker/test_ptr_public_test.go similarity index 100% rename from internal/api/container/test_ptr_public_test.go rename to internal/api/docker/test_ptr_public_test.go diff --git a/internal/api/container/types.go b/internal/api/docker/types.go similarity index 100% rename from internal/api/container/types.go rename to internal/api/docker/types.go diff --git a/internal/api/container/validate.go b/internal/api/docker/validate.go similarity index 100% rename from internal/api/container/validate.go rename to internal/api/docker/validate.go diff --git a/internal/api/gen/api.yaml b/internal/api/gen/api.yaml index c4c13505..d6ec424a 100644 --- a/internal/api/gen/api.yaml +++ b/internal/api/gen/api.yaml @@ -12,15 +12,15 @@ tags: - name: OSAPI_-_A_CRUD_API_for_managing_Linux_systems_info x-displayName: Info description: Operations related to the info endpoint. - - name: Container_Management_API_container_operations - x-displayName: Node/Container - description: Container lifecycle operations on a target node. - - name: Container_Management_API_container_exec - x-displayName: Node/Container/Exec + - name: Docker_Management_API_docker_operations + x-displayName: Node/Docker + description: Docker lifecycle operations on a target node. + - name: Docker_Management_API_docker_exec + x-displayName: Node/Docker/Exec description: Command execution inside containers on a target node. - - name: Container_Management_API_container_image - x-displayName: Node/Container/Image - description: Container image operations on a target node. + - name: Docker_Management_API_docker_image + x-displayName: Node/Docker/Image + description: Docker image operations on a target node. - name: File_Management_API_file_operations x-displayName: File description: Object Store file management operations. @@ -436,7 +436,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container: + /node/{hostname}/container/docker: servers: [] post: summary: Create a container @@ -444,11 +444,11 @@ paths: Create a new container on the target node. Returns a job ID for tracking the asynchronous operation. tags: - - Container_Management_API_container_operations - operationId: PostNodeContainer + - Docker_Management_API_docker_operations + operationId: PostNodeContainerDocker security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' requestBody: @@ -457,14 +457,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerCreateRequest' + $ref: '#/components/schemas/DockerCreateRequest' responses: '202': description: Container creation accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerResultCollectionResponse' + $ref: '#/components/schemas/DockerResultCollectionResponse' '400': description: Invalid request payload. content: @@ -494,11 +494,11 @@ paths: description: | List containers on the target node, optionally filtered by state. tags: - - Container_Management_API_container_operations - operationId: GetNodeContainer + - Docker_Management_API_docker_operations + operationId: GetNodeContainerDocker security: - BearerAuth: - - container:read + - docker:read parameters: - $ref: '#/components/parameters/Hostname' - name: state @@ -533,7 +533,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerListCollectionResponse' + $ref: '#/components/schemas/DockerListCollectionResponse' '400': description: Invalid request parameters. content: @@ -558,28 +558,28 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}: + /node/{hostname}/container/docker/{id}: servers: [] get: summary: Inspect a container description: | Get detailed information about a specific container. tags: - - Container_Management_API_container_operations - operationId: GetNodeContainerByID + - Docker_Management_API_docker_operations + operationId: GetNodeContainerDockerByID security: - BearerAuth: - - container:read + - docker:read parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' responses: '200': description: Container detail. content: application/json: schema: - $ref: '#/components/schemas/ContainerDetailCollectionResponse' + $ref: '#/components/schemas/DockerDetailCollectionResponse' '400': description: Invalid request parameters. content: @@ -616,14 +616,14 @@ paths: Remove a container from the target node. Use the force query parameter to remove a running container. tags: - - Container_Management_API_container_operations - operationId: DeleteNodeContainerByID + - Docker_Management_API_docker_operations + operationId: DeleteNodeContainerDockerByID security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' - name: force in: query required: false @@ -640,7 +640,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -671,28 +671,28 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}/start: + /node/{hostname}/container/docker/{id}/start: servers: [] post: summary: Start a container description: | Start a stopped container on the target node. tags: - - Container_Management_API_container_operations - operationId: PostNodeContainerStart + - Docker_Management_API_docker_operations + operationId: PostNodeContainerDockerStart security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' responses: '202': description: Container start accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -723,7 +723,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}/stop: + /node/{hostname}/container/docker/{id}/stop: servers: [] post: summary: Stop a container @@ -731,28 +731,28 @@ paths: Stop a running container on the target node. Optionally specify a timeout before the container is killed. tags: - - Container_Management_API_container_operations - operationId: PostNodeContainerStop + - Docker_Management_API_docker_operations + operationId: PostNodeContainerDockerStop security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' requestBody: description: Optional stop parameters. required: false content: application/json: schema: - $ref: '#/components/schemas/ContainerStopRequest' + $ref: '#/components/schemas/DockerStopRequest' responses: '202': description: Container stop accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerActionCollectionResponse' + $ref: '#/components/schemas/DockerActionCollectionResponse' '400': description: Invalid request parameters. content: @@ -783,35 +783,35 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container/{id}/exec: + /node/{hostname}/container/docker/{id}/exec: servers: [] post: summary: Execute a command in a container description: | Execute a command inside a running container on the target node. tags: - - Container_Management_API_container_exec - operationId: PostNodeContainerExec + - Docker_Management_API_docker_exec + operationId: PostNodeContainerDockerExec security: - BearerAuth: - - container:execute + - docker:execute parameters: - $ref: '#/components/parameters/Hostname' - - $ref: '#/components/parameters/ContainerId' + - $ref: '#/components/parameters/DockerId' requestBody: description: The command to execute inside the container. required: true content: application/json: schema: - $ref: '#/components/schemas/ContainerExecRequest' + $ref: '#/components/schemas/DockerExecRequest' responses: '202': description: Container exec accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerExecCollectionResponse' + $ref: '#/components/schemas/DockerExecCollectionResponse' '400': description: Invalid request payload. content: @@ -842,18 +842,18 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /node/{hostname}/container/pull: + /node/{hostname}/container/docker/pull: servers: [] post: summary: Pull a container image description: | Pull a container image on the target node. tags: - - Container_Management_API_container_image - operationId: PostNodeContainerPull + - Docker_Management_API_docker_image + operationId: PostNodeContainerDockerPull security: - BearerAuth: - - container:write + - docker:write parameters: - $ref: '#/components/parameters/Hostname' requestBody: @@ -862,14 +862,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerPullRequest' + $ref: '#/components/schemas/DockerPullRequest' responses: '202': description: Image pull accepted. content: application/json: schema: - $ref: '#/components/schemas/ContainerPullCollectionResponse' + $ref: '#/components/schemas/DockerPullCollectionResponse' '400': description: Invalid request payload. content: @@ -2492,7 +2492,7 @@ components: required: - total_items - items - ContainerCreateRequest: + DockerCreateRequest: type: object properties: image: @@ -2547,7 +2547,7 @@ components: default: true required: - image - ContainerStopRequest: + DockerStopRequest: type: object properties: timeout: @@ -2559,7 +2559,7 @@ components: default: 10 x-oapi-codegen-extra-tags: validate: omitempty,min=0,max=300 - ContainerExecRequest: + DockerExecRequest: type: object properties: command: @@ -2584,7 +2584,7 @@ components: example: /app required: - command - ContainerPullRequest: + DockerPullRequest: type: object properties: image: @@ -2597,7 +2597,7 @@ components: validate: required,min=1 required: - image - ContainerResponse: + DockerResponse: type: object description: Summary information about a container. properties: @@ -2632,7 +2632,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerDetailResponse: + DockerDetailResponse: type: object description: Detailed information about a container. properties: @@ -2695,7 +2695,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerActionResultItem: + DockerActionResultItem: type: object description: Result of a container lifecycle action. properties: @@ -2717,7 +2717,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerExecResultItem: + DockerExecResultItem: type: object description: Result of a command execution inside a container. properties: @@ -2741,7 +2741,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerPullResultItem: + DockerPullResultItem: type: object description: Result of an image pull operation. properties: @@ -2768,7 +2768,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerListItem: + DockerListItem: type: object description: Container summary for list operations. properties: @@ -2779,7 +2779,7 @@ components: type: array description: List of containers on this agent. items: - $ref: '#/components/schemas/ContainerSummary' + $ref: '#/components/schemas/DockerSummary' changed: type: boolean description: Whether the operation modified system state. @@ -2788,7 +2788,7 @@ components: description: Error message if the agent failed. required: - hostname - ContainerSummary: + DockerSummary: type: object description: Brief container summary. properties: @@ -2812,7 +2812,7 @@ components: type: string description: Container creation timestamp. example: '2024-01-15T10:30:00Z' - ContainerResultCollectionResponse: + DockerResultCollectionResponse: type: object properties: job_id: @@ -2823,10 +2823,10 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerResponse' + $ref: '#/components/schemas/DockerResponse' required: - results - ContainerListCollectionResponse: + DockerListCollectionResponse: type: object properties: job_id: @@ -2837,10 +2837,10 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerListItem' + $ref: '#/components/schemas/DockerListItem' required: - results - ContainerDetailCollectionResponse: + DockerDetailCollectionResponse: type: object properties: job_id: @@ -2851,10 +2851,10 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerDetailResponse' + $ref: '#/components/schemas/DockerDetailResponse' required: - results - ContainerActionCollectionResponse: + DockerActionCollectionResponse: type: object properties: job_id: @@ -2865,10 +2865,10 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerActionResultItem' + $ref: '#/components/schemas/DockerActionResultItem' required: - results - ContainerExecCollectionResponse: + DockerExecCollectionResponse: type: object properties: job_id: @@ -2879,10 +2879,10 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerExecResultItem' + $ref: '#/components/schemas/DockerExecResultItem' required: - results - ContainerPullCollectionResponse: + DockerPullCollectionResponse: type: object properties: job_id: @@ -2893,7 +2893,7 @@ components: results: type: array items: - $ref: '#/components/schemas/ContainerPullResultItem' + $ref: '#/components/schemas/DockerPullResultItem' required: - results FileInfo: @@ -4028,7 +4028,7 @@ components: schema: type: string minLength: 1 - ContainerId: + DockerId: name: id in: path required: true @@ -4064,11 +4064,11 @@ x-tagGroups: - name: OSAPI - A CRUD API for managing Linux systems tags: - OSAPI_-_A_CRUD_API_for_managing_Linux_systems_info - - name: Container Management API + - name: Docker Management API tags: - - Container_Management_API_container_operations - - Container_Management_API_container_exec - - Container_Management_API_container_image + - Docker_Management_API_docker_operations + - Docker_Management_API_docker_exec + - Docker_Management_API_docker_image - name: File Management API tags: - File_Management_API_file_operations diff --git a/internal/api/handler_container.go b/internal/api/handler_docker.go similarity index 78% rename from internal/api/handler_container.go rename to internal/api/handler_docker.go index a283cf6d..1d672437 100644 --- a/internal/api/handler_container.go +++ b/internal/api/handler_docker.go @@ -24,29 +24,29 @@ import ( "github.com/labstack/echo/v4" strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" - "github.com/retr0h/osapi/internal/api/container" - containerGen "github.com/retr0h/osapi/internal/api/container/gen" + docker "github.com/retr0h/osapi/internal/api/docker" + dockerGen "github.com/retr0h/osapi/internal/api/docker/gen" "github.com/retr0h/osapi/internal/authtoken" "github.com/retr0h/osapi/internal/job/client" ) -// GetContainerHandler returns container handler for registration. -func (s *Server) GetContainerHandler( +// GetDockerHandler returns Docker handler for registration. +func (s *Server) GetDockerHandler( jobClient client.JobClient, ) []func(e *echo.Echo) { var tokenManager TokenValidator = authtoken.New(s.logger) - containerHandler := container.New(s.logger, jobClient) + dockerHandler := docker.New(s.logger, jobClient) - strictHandler := containerGen.NewStrictHandler( - containerHandler, - []containerGen.StrictMiddlewareFunc{ + strictHandler := dockerGen.NewStrictHandler( + dockerHandler, + []dockerGen.StrictMiddlewareFunc{ func(handler strictecho.StrictEchoHandlerFunc, _ string) strictecho.StrictEchoHandlerFunc { return scopeMiddleware( handler, tokenManager, s.appConfig.API.Server.Security.SigningKey, - containerGen.BearerAuthScopes, + dockerGen.BearerAuthScopes, s.customRoles, ) }, @@ -55,7 +55,7 @@ func (s *Server) GetContainerHandler( return []func(e *echo.Echo){ func(e *echo.Echo) { - containerGen.RegisterHandlers(e, strictHandler) + dockerGen.RegisterHandlers(e, strictHandler) }, } } diff --git a/internal/api/handler_public_test.go b/internal/api/handler_public_test.go index fee2eab1..5ea228d9 100644 --- a/internal/api/handler_public_test.go +++ b/internal/api/handler_public_test.go @@ -379,13 +379,13 @@ func (s *HandlerPublicTestSuite) TestGetFileHandler() { } } -func (s *HandlerPublicTestSuite) TestGetContainerHandler() { +func (s *HandlerPublicTestSuite) TestGetDockerHandler() { tests := []struct { name string validate func([]func(e *echo.Echo)) }{ { - name: "returns container handler functions", + name: "returns docker handler functions", validate: func(handlers []func(e *echo.Echo)) { s.NotEmpty(handlers) }, @@ -399,7 +399,7 @@ func (s *HandlerPublicTestSuite) TestGetContainerHandler() { } s.NotEmpty(e.Routes()) - req := httptest.NewRequest(http.MethodGet, "/node/hostname/container", nil) + req := httptest.NewRequest(http.MethodGet, "/node/hostname/container/docker", nil) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) }, @@ -408,7 +408,7 @@ func (s *HandlerPublicTestSuite) TestGetContainerHandler() { for _, tt := range tests { s.Run(tt.name, func() { - handlers := s.server.GetContainerHandler(s.mockJobClient) + handlers := s.server.GetDockerHandler(s.mockJobClient) tt.validate(handlers) }) diff --git a/internal/authtoken/permissions.go b/internal/authtoken/permissions.go index ebdd1b7c..0638cdb5 100644 --- a/internal/authtoken/permissions.go +++ b/internal/authtoken/permissions.go @@ -25,21 +25,21 @@ type Permission = string // Permission constants using resource:verb format. const ( - PermAgentRead Permission = "agent:read" - PermAgentWrite Permission = "agent:write" - PermNodeRead Permission = "node:read" - PermNetworkRead Permission = "network:read" - PermNetworkWrite Permission = "network:write" - PermJobRead Permission = "job:read" - PermJobWrite Permission = "job:write" - PermHealthRead Permission = "health:read" - PermAuditRead Permission = "audit:read" - PermCommandExecute Permission = "command:execute" - PermFileRead Permission = "file:read" - PermFileWrite Permission = "file:write" - PermContainerRead Permission = "container:read" - PermContainerWrite Permission = "container:write" - PermContainerExecute Permission = "container:execute" + PermAgentRead Permission = "agent:read" + PermAgentWrite Permission = "agent:write" + PermNodeRead Permission = "node:read" + PermNetworkRead Permission = "network:read" + PermNetworkWrite Permission = "network:write" + PermJobRead Permission = "job:read" + PermJobWrite Permission = "job:write" + PermHealthRead Permission = "health:read" + PermAuditRead Permission = "audit:read" + PermCommandExecute Permission = "command:execute" + PermFileRead Permission = "file:read" + PermFileWrite Permission = "file:write" + PermDockerRead Permission = "docker:read" + PermDockerWrite Permission = "docker:write" + PermDockerExecute Permission = "docker:execute" ) // AllPermissions is the full set of known permissions. @@ -56,9 +56,9 @@ var AllPermissions = []Permission{ PermCommandExecute, PermFileRead, PermFileWrite, - PermContainerRead, - PermContainerWrite, - PermContainerExecute, + PermDockerRead, + PermDockerWrite, + PermDockerExecute, } // DefaultRolePermissions maps built-in role names to their granted permissions. @@ -76,9 +76,9 @@ var DefaultRolePermissions = map[string][]Permission{ PermCommandExecute, PermFileRead, PermFileWrite, - PermContainerRead, - PermContainerWrite, - PermContainerExecute, + PermDockerRead, + PermDockerWrite, + PermDockerExecute, }, "write": { PermAgentRead, @@ -90,8 +90,8 @@ var DefaultRolePermissions = map[string][]Permission{ PermHealthRead, PermFileRead, PermFileWrite, - PermContainerRead, - PermContainerWrite, + PermDockerRead, + PermDockerWrite, }, "read": { PermAgentRead, @@ -100,7 +100,7 @@ var DefaultRolePermissions = map[string][]Permission{ PermJobRead, PermHealthRead, PermFileRead, - PermContainerRead, + PermDockerRead, }, } diff --git a/internal/job/client/modify_container.go b/internal/job/client/modify_docker.go similarity index 80% rename from internal/job/client/modify_container.go rename to internal/job/client/modify_docker.go index 91cc7d97..c50b9371 100644 --- a/internal/job/client/modify_container.go +++ b/internal/job/client/modify_docker.go @@ -28,17 +28,17 @@ import ( "github.com/retr0h/osapi/internal/job" ) -// ModifyContainerCreate creates a container on a target. -func (c *Client) ModifyContainerCreate( +// ModifyDockerCreate creates a docker container on a target. +func (c *Client) ModifyDockerCreate( ctx context.Context, target string, - data *job.ContainerCreateData, + data *job.DockerCreateData, ) (*job.Response, error) { dataBytes, _ := json.Marshal(data) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerCreate, + Category: "docker", + Operation: job.OperationDockerCreate, Data: json.RawMessage(dataBytes), } @@ -57,8 +57,8 @@ func (c *Client) ModifyContainerCreate( return resp, nil } -// ModifyContainerStart starts a container on a target. -func (c *Client) ModifyContainerStart( +// ModifyDockerStart starts a docker container on a target. +func (c *Client) ModifyDockerStart( ctx context.Context, target string, id string, @@ -67,8 +67,8 @@ func (c *Client) ModifyContainerStart( dataBytes, _ := json.Marshal(data) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerStart, + Category: "docker", + Operation: job.OperationDockerStart, Data: json.RawMessage(dataBytes), } @@ -87,12 +87,12 @@ func (c *Client) ModifyContainerStart( return resp, nil } -// ModifyContainerStop stops a container on a target. -func (c *Client) ModifyContainerStop( +// ModifyDockerStop stops a docker container on a target. +func (c *Client) ModifyDockerStop( ctx context.Context, target string, id string, - data *job.ContainerStopData, + data *job.DockerStopData, ) (*job.Response, error) { // Merge id into the data stopData := struct { @@ -105,8 +105,8 @@ func (c *Client) ModifyContainerStop( dataBytes, _ := json.Marshal(stopData) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerStop, + Category: "docker", + Operation: job.OperationDockerStop, Data: json.RawMessage(dataBytes), } @@ -125,12 +125,12 @@ func (c *Client) ModifyContainerStop( return resp, nil } -// ModifyContainerRemove removes a container on a target. -func (c *Client) ModifyContainerRemove( +// ModifyDockerRemove removes a docker container on a target. +func (c *Client) ModifyDockerRemove( ctx context.Context, target string, id string, - data *job.ContainerRemoveData, + data *job.DockerRemoveData, ) (*job.Response, error) { // Merge id into the data removeData := struct { @@ -143,8 +143,8 @@ func (c *Client) ModifyContainerRemove( dataBytes, _ := json.Marshal(removeData) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerRemove, + Category: "docker", + Operation: job.OperationDockerRemove, Data: json.RawMessage(dataBytes), } @@ -163,17 +163,17 @@ func (c *Client) ModifyContainerRemove( return resp, nil } -// QueryContainerList lists containers on a target. -func (c *Client) QueryContainerList( +// QueryDockerList lists docker containers on a target. +func (c *Client) QueryDockerList( ctx context.Context, target string, - data *job.ContainerListData, + data *job.DockerListData, ) (*job.Response, error) { dataBytes, _ := json.Marshal(data) req := &job.Request{ Type: job.TypeQuery, - Category: "container", - Operation: job.OperationContainerList, + Category: "docker", + Operation: job.OperationDockerList, Data: json.RawMessage(dataBytes), } @@ -192,8 +192,8 @@ func (c *Client) QueryContainerList( return resp, nil } -// QueryContainerInspect inspects a container on a target. -func (c *Client) QueryContainerInspect( +// QueryDockerInspect inspects a docker container on a target. +func (c *Client) QueryDockerInspect( ctx context.Context, target string, id string, @@ -202,8 +202,8 @@ func (c *Client) QueryContainerInspect( dataBytes, _ := json.Marshal(data) req := &job.Request{ Type: job.TypeQuery, - Category: "container", - Operation: job.OperationContainerInspect, + Category: "docker", + Operation: job.OperationDockerInspect, Data: json.RawMessage(dataBytes), } @@ -222,12 +222,12 @@ func (c *Client) QueryContainerInspect( return resp, nil } -// ModifyContainerExec executes a command in a container on a target. -func (c *Client) ModifyContainerExec( +// ModifyDockerExec executes a command in a docker container on a target. +func (c *Client) ModifyDockerExec( ctx context.Context, target string, id string, - data *job.ContainerExecData, + data *job.DockerExecData, ) (*job.Response, error) { // Merge id into the data execData := struct { @@ -244,8 +244,8 @@ func (c *Client) ModifyContainerExec( dataBytes, _ := json.Marshal(execData) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerExec, + Category: "docker", + Operation: job.OperationDockerExec, Data: json.RawMessage(dataBytes), } @@ -264,17 +264,17 @@ func (c *Client) ModifyContainerExec( return resp, nil } -// ModifyContainerPull pulls an image on a target. -func (c *Client) ModifyContainerPull( +// ModifyDockerPull pulls a docker image on a target. +func (c *Client) ModifyDockerPull( ctx context.Context, target string, - data *job.ContainerPullData, + data *job.DockerPullData, ) (*job.Response, error) { dataBytes, _ := json.Marshal(data) req := &job.Request{ Type: job.TypeModify, - Category: "container", - Operation: job.OperationContainerPull, + Category: "docker", + Operation: job.OperationDockerPull, Data: json.RawMessage(dataBytes), } diff --git a/internal/job/client/modify_container_public_test.go b/internal/job/client/modify_docker_public_test.go similarity index 85% rename from internal/job/client/modify_container_public_test.go rename to internal/job/client/modify_docker_public_test.go index e184877e..2917d355 100644 --- a/internal/job/client/modify_container_public_test.go +++ b/internal/job/client/modify_docker_public_test.go @@ -35,7 +35,7 @@ import ( jobmocks "github.com/retr0h/osapi/internal/job/mocks" ) -type ModifyContainerPublicTestSuite struct { +type ModifyDockerPublicTestSuite struct { suite.Suite mockCtrl *gomock.Controller @@ -45,7 +45,7 @@ type ModifyContainerPublicTestSuite struct { ctx context.Context } -func (s *ModifyContainerPublicTestSuite) SetupTest() { +func (s *ModifyDockerPublicTestSuite) SetupTest() { s.mockCtrl = gomock.NewController(s.T()) s.mockNATSClient = jobmocks.NewMockNATSClient(s.mockCtrl) s.mockKV = jobmocks.NewMockKeyValue(s.mockCtrl) @@ -60,15 +60,15 @@ func (s *ModifyContainerPublicTestSuite) SetupTest() { s.Require().NoError(err) } -func (s *ModifyContainerPublicTestSuite) TearDownTest() { +func (s *ModifyDockerPublicTestSuite) TearDownTest() { s.mockCtrl.Finish() } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerCreate() { tests := []struct { name string target string - data *job.ContainerCreateData + data *job.DockerCreateData responseData string mockError error expectError bool @@ -77,7 +77,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { { name: "success", target: "server1", - data: &job.ContainerCreateData{ + data: &job.DockerCreateData{ Image: "nginx:latest", Name: "test-nginx", }, @@ -90,7 +90,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { { name: "job failed", target: "server1", - data: &job.ContainerCreateData{ + data: &job.DockerCreateData{ Image: "invalid:image", }, responseData: `{ @@ -103,7 +103,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { { name: "publish error", target: "server1", - data: &job.ContainerCreateData{ + data: &job.DockerCreateData{ Image: "nginx:latest", }, mockError: errors.New("connection failed"), @@ -123,7 +123,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerCreate( + resp, err := s.jobsClient.ModifyDockerCreate( s.ctx, tt.target, tt.data, @@ -143,7 +143,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerCreate() { } } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerStart() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerStart() { tests := []struct { name string target string @@ -195,7 +195,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStart() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerStart( + resp, err := s.jobsClient.ModifyDockerStart( s.ctx, tt.target, tt.id, @@ -215,13 +215,13 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStart() { } } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerStop() { timeout := 10 tests := []struct { name string target string id string - data *job.ContainerStopData + data *job.DockerStopData responseData string mockError error expectError bool @@ -231,7 +231,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { name: "success with timeout", target: "server1", id: "abc123", - data: &job.ContainerStopData{Timeout: &timeout}, + data: &job.DockerStopData{Timeout: &timeout}, responseData: `{ "status": "completed", "data": {"message":"Container stopped successfully"} @@ -242,7 +242,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { name: "success without timeout", target: "server1", id: "abc123", - data: &job.ContainerStopData{}, + data: &job.DockerStopData{}, responseData: `{ "status": "completed", "data": {"message":"Container stopped successfully"} @@ -253,7 +253,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { name: "job failed", target: "server1", id: "abc123", - data: &job.ContainerStopData{}, + data: &job.DockerStopData{}, responseData: `{ "status": "failed", "error": "container not running" @@ -265,7 +265,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { name: "publish error", target: "server1", id: "abc123", - data: &job.ContainerStopData{}, + data: &job.DockerStopData{}, mockError: errors.New("connection failed"), expectError: true, errorContains: "failed to publish and wait", @@ -283,7 +283,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerStop( + resp, err := s.jobsClient.ModifyDockerStop( s.ctx, tt.target, tt.id, @@ -304,12 +304,12 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerStop() { } } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerRemove() { tests := []struct { name string target string id string - data *job.ContainerRemoveData + data *job.DockerRemoveData responseData string mockError error expectError bool @@ -319,7 +319,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { name: "success with force", target: "server1", id: "abc123", - data: &job.ContainerRemoveData{Force: true}, + data: &job.DockerRemoveData{Force: true}, responseData: `{ "status": "completed", "data": {"message":"Container removed successfully"} @@ -330,7 +330,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { name: "success without force", target: "server1", id: "abc123", - data: &job.ContainerRemoveData{Force: false}, + data: &job.DockerRemoveData{Force: false}, responseData: `{ "status": "completed", "data": {"message":"Container removed successfully"} @@ -341,7 +341,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { name: "job failed", target: "server1", id: "abc123", - data: &job.ContainerRemoveData{}, + data: &job.DockerRemoveData{}, responseData: `{ "status": "failed", "error": "container is running" @@ -353,7 +353,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { name: "publish error", target: "server1", id: "abc123", - data: &job.ContainerRemoveData{}, + data: &job.DockerRemoveData{}, mockError: errors.New("connection failed"), expectError: true, errorContains: "failed to publish and wait", @@ -371,7 +371,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerRemove( + resp, err := s.jobsClient.ModifyDockerRemove( s.ctx, tt.target, tt.id, @@ -392,11 +392,11 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerRemove() { } } -func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { +func (s *ModifyDockerPublicTestSuite) TestQueryDockerList() { tests := []struct { name string target string - data *job.ContainerListData + data *job.DockerListData responseData string mockError error expectError bool @@ -405,7 +405,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { { name: "success", target: "server1", - data: &job.ContainerListData{ + data: &job.DockerListData{ State: "running", Limit: 10, }, @@ -418,7 +418,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { { name: "job failed", target: "server1", - data: &job.ContainerListData{}, + data: &job.DockerListData{}, responseData: `{ "status": "failed", "error": "runtime not available" @@ -429,7 +429,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { { name: "publish error", target: "server1", - data: &job.ContainerListData{}, + data: &job.DockerListData{}, mockError: errors.New("connection failed"), expectError: true, errorContains: "failed to publish and wait", @@ -447,7 +447,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { tt.mockError, ) - resp, err := s.jobsClient.QueryContainerList( + resp, err := s.jobsClient.QueryDockerList( s.ctx, tt.target, tt.data, @@ -467,7 +467,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerList() { } } -func (s *ModifyContainerPublicTestSuite) TestQueryContainerInspect() { +func (s *ModifyDockerPublicTestSuite) TestQueryDockerInspect() { tests := []struct { name string target string @@ -519,7 +519,7 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerInspect() { tt.mockError, ) - resp, err := s.jobsClient.QueryContainerInspect( + resp, err := s.jobsClient.QueryDockerInspect( s.ctx, tt.target, tt.id, @@ -539,12 +539,12 @@ func (s *ModifyContainerPublicTestSuite) TestQueryContainerInspect() { } } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerExec() { tests := []struct { name string target string id string - data *job.ContainerExecData + data *job.DockerExecData responseData string mockError error expectError bool @@ -554,7 +554,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { name: "success", target: "server1", id: "abc123", - data: &job.ContainerExecData{ + data: &job.DockerExecData{ Command: []string{"ls", "-la"}, }, responseData: `{ @@ -567,7 +567,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { name: "job failed", target: "server1", id: "abc123", - data: &job.ContainerExecData{ + data: &job.DockerExecData{ Command: []string{"bad-cmd"}, }, responseData: `{ @@ -581,7 +581,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { name: "publish error", target: "server1", id: "abc123", - data: &job.ContainerExecData{ + data: &job.DockerExecData{ Command: []string{"ls"}, }, mockError: errors.New("connection failed"), @@ -601,7 +601,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerExec( + resp, err := s.jobsClient.ModifyDockerExec( s.ctx, tt.target, tt.id, @@ -622,11 +622,11 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerExec() { } } -func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { +func (s *ModifyDockerPublicTestSuite) TestModifyDockerPull() { tests := []struct { name string target string - data *job.ContainerPullData + data *job.DockerPullData responseData string mockError error expectError bool @@ -635,7 +635,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { { name: "success", target: "server1", - data: &job.ContainerPullData{ + data: &job.DockerPullData{ Image: "nginx:latest", }, responseData: `{ @@ -647,7 +647,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { { name: "job failed", target: "server1", - data: &job.ContainerPullData{ + data: &job.DockerPullData{ Image: "invalid:image", }, responseData: `{ @@ -660,7 +660,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { { name: "publish error", target: "server1", - data: &job.ContainerPullData{ + data: &job.DockerPullData{ Image: "nginx:latest", }, mockError: errors.New("connection failed"), @@ -680,7 +680,7 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { tt.mockError, ) - resp, err := s.jobsClient.ModifyContainerPull( + resp, err := s.jobsClient.ModifyDockerPull( s.ctx, tt.target, tt.data, @@ -700,6 +700,6 @@ func (s *ModifyContainerPublicTestSuite) TestModifyContainerPull() { } } -func TestModifyContainerPublicTestSuite(t *testing.T) { - suite.Run(t, new(ModifyContainerPublicTestSuite)) +func TestModifyDockerPublicTestSuite(t *testing.T) { + suite.Run(t, new(ModifyDockerPublicTestSuite)) } diff --git a/internal/job/client/types.go b/internal/job/client/types.go index ebf630cd..3a23e9b8 100644 --- a/internal/job/client/types.go +++ b/internal/job/client/types.go @@ -237,49 +237,49 @@ type JobClient interface { path string, ) (string, *file.StatusResult, string, error) - // Container operations - ModifyContainerCreate( + // Docker operations + ModifyDockerCreate( ctx context.Context, target string, - data *job.ContainerCreateData, + data *job.DockerCreateData, ) (*job.Response, error) - ModifyContainerStart( + ModifyDockerStart( ctx context.Context, target string, id string, ) (*job.Response, error) - ModifyContainerStop( + ModifyDockerStop( ctx context.Context, target string, id string, - data *job.ContainerStopData, + data *job.DockerStopData, ) (*job.Response, error) - ModifyContainerRemove( + ModifyDockerRemove( ctx context.Context, target string, id string, - data *job.ContainerRemoveData, + data *job.DockerRemoveData, ) (*job.Response, error) - QueryContainerList( + QueryDockerList( ctx context.Context, target string, - data *job.ContainerListData, + data *job.DockerListData, ) (*job.Response, error) - QueryContainerInspect( + QueryDockerInspect( ctx context.Context, target string, id string, ) (*job.Response, error) - ModifyContainerExec( + ModifyDockerExec( ctx context.Context, target string, id string, - data *job.ContainerExecData, + data *job.DockerExecData, ) (*job.Response, error) - ModifyContainerPull( + ModifyDockerPull( ctx context.Context, target string, - data *job.ContainerPullData, + data *job.DockerPullData, ) (*job.Response, error) // Agent discovery diff --git a/internal/job/mocks/job_client.gen.go b/internal/job/mocks/job_client.gen.go index b3e583bb..f5e7e0fc 100644 --- a/internal/job/mocks/job_client.gen.go +++ b/internal/job/mocks/job_client.gen.go @@ -288,94 +288,94 @@ func (mr *MockJobClientMockRecorder) ModifyCommandShellBroadcast(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyCommandShellBroadcast", reflect.TypeOf((*MockJobClient)(nil).ModifyCommandShellBroadcast), arg0, arg1, arg2, arg3, arg4) } -// ModifyContainerCreate mocks base method. -func (m *MockJobClient) ModifyContainerCreate(arg0 context.Context, arg1 string, arg2 *job.ContainerCreateData) (*job.Response, error) { +// ModifyDockerCreate mocks base method. +func (m *MockJobClient) ModifyDockerCreate(arg0 context.Context, arg1 string, arg2 *job.DockerCreateData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerCreate", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ModifyDockerCreate", arg0, arg1, arg2) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerCreate indicates an expected call of ModifyContainerCreate. -func (mr *MockJobClientMockRecorder) ModifyContainerCreate(arg0, arg1, arg2 interface{}) *gomock.Call { +// ModifyDockerCreate indicates an expected call of ModifyDockerCreate. +func (mr *MockJobClientMockRecorder) ModifyDockerCreate(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerCreate", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerCreate), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerCreate", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerCreate), arg0, arg1, arg2) } -// ModifyContainerExec mocks base method. -func (m *MockJobClient) ModifyContainerExec(arg0 context.Context, arg1, arg2 string, arg3 *job.ContainerExecData) (*job.Response, error) { +// ModifyDockerExec mocks base method. +func (m *MockJobClient) ModifyDockerExec(arg0 context.Context, arg1, arg2 string, arg3 *job.DockerExecData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerExec", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "ModifyDockerExec", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerExec indicates an expected call of ModifyContainerExec. -func (mr *MockJobClientMockRecorder) ModifyContainerExec(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// ModifyDockerExec indicates an expected call of ModifyDockerExec. +func (mr *MockJobClientMockRecorder) ModifyDockerExec(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerExec", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerExec), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerExec", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerExec), arg0, arg1, arg2, arg3) } -// ModifyContainerPull mocks base method. -func (m *MockJobClient) ModifyContainerPull(arg0 context.Context, arg1 string, arg2 *job.ContainerPullData) (*job.Response, error) { +// ModifyDockerPull mocks base method. +func (m *MockJobClient) ModifyDockerPull(arg0 context.Context, arg1 string, arg2 *job.DockerPullData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerPull", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ModifyDockerPull", arg0, arg1, arg2) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerPull indicates an expected call of ModifyContainerPull. -func (mr *MockJobClientMockRecorder) ModifyContainerPull(arg0, arg1, arg2 interface{}) *gomock.Call { +// ModifyDockerPull indicates an expected call of ModifyDockerPull. +func (mr *MockJobClientMockRecorder) ModifyDockerPull(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerPull", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerPull), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerPull", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerPull), arg0, arg1, arg2) } -// ModifyContainerRemove mocks base method. -func (m *MockJobClient) ModifyContainerRemove(arg0 context.Context, arg1, arg2 string, arg3 *job.ContainerRemoveData) (*job.Response, error) { +// ModifyDockerRemove mocks base method. +func (m *MockJobClient) ModifyDockerRemove(arg0 context.Context, arg1, arg2 string, arg3 *job.DockerRemoveData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerRemove", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "ModifyDockerRemove", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerRemove indicates an expected call of ModifyContainerRemove. -func (mr *MockJobClientMockRecorder) ModifyContainerRemove(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// ModifyDockerRemove indicates an expected call of ModifyDockerRemove. +func (mr *MockJobClientMockRecorder) ModifyDockerRemove(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerRemove", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerRemove), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerRemove", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerRemove), arg0, arg1, arg2, arg3) } -// ModifyContainerStart mocks base method. -func (m *MockJobClient) ModifyContainerStart(arg0 context.Context, arg1, arg2 string) (*job.Response, error) { +// ModifyDockerStart mocks base method. +func (m *MockJobClient) ModifyDockerStart(arg0 context.Context, arg1, arg2 string) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerStart", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ModifyDockerStart", arg0, arg1, arg2) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerStart indicates an expected call of ModifyContainerStart. -func (mr *MockJobClientMockRecorder) ModifyContainerStart(arg0, arg1, arg2 interface{}) *gomock.Call { +// ModifyDockerStart indicates an expected call of ModifyDockerStart. +func (mr *MockJobClientMockRecorder) ModifyDockerStart(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerStart", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerStart), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerStart", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerStart), arg0, arg1, arg2) } -// ModifyContainerStop mocks base method. -func (m *MockJobClient) ModifyContainerStop(arg0 context.Context, arg1, arg2 string, arg3 *job.ContainerStopData) (*job.Response, error) { +// ModifyDockerStop mocks base method. +func (m *MockJobClient) ModifyDockerStop(arg0 context.Context, arg1, arg2 string, arg3 *job.DockerStopData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyContainerStop", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "ModifyDockerStop", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModifyContainerStop indicates an expected call of ModifyContainerStop. -func (mr *MockJobClientMockRecorder) ModifyContainerStop(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// ModifyDockerStop indicates an expected call of ModifyDockerStop. +func (mr *MockJobClientMockRecorder) ModifyDockerStop(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyContainerStop", reflect.TypeOf((*MockJobClient)(nil).ModifyContainerStop), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDockerStop", reflect.TypeOf((*MockJobClient)(nil).ModifyDockerStop), arg0, arg1, arg2, arg3) } // ModifyFileDeploy mocks base method. @@ -463,34 +463,34 @@ func (mr *MockJobClientMockRecorder) ModifyNetworkDNSBroadcast(arg0, arg1, arg2, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNetworkDNSBroadcast", reflect.TypeOf((*MockJobClient)(nil).ModifyNetworkDNSBroadcast), arg0, arg1, arg2, arg3, arg4) } -// QueryContainerInspect mocks base method. -func (m *MockJobClient) QueryContainerInspect(arg0 context.Context, arg1, arg2 string) (*job.Response, error) { +// QueryDockerInspect mocks base method. +func (m *MockJobClient) QueryDockerInspect(arg0 context.Context, arg1, arg2 string) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryContainerInspect", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "QueryDockerInspect", arg0, arg1, arg2) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// QueryContainerInspect indicates an expected call of QueryContainerInspect. -func (mr *MockJobClientMockRecorder) QueryContainerInspect(arg0, arg1, arg2 interface{}) *gomock.Call { +// QueryDockerInspect indicates an expected call of QueryDockerInspect. +func (mr *MockJobClientMockRecorder) QueryDockerInspect(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryContainerInspect", reflect.TypeOf((*MockJobClient)(nil).QueryContainerInspect), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryDockerInspect", reflect.TypeOf((*MockJobClient)(nil).QueryDockerInspect), arg0, arg1, arg2) } -// QueryContainerList mocks base method. -func (m *MockJobClient) QueryContainerList(arg0 context.Context, arg1 string, arg2 *job.ContainerListData) (*job.Response, error) { +// QueryDockerList mocks base method. +func (m *MockJobClient) QueryDockerList(arg0 context.Context, arg1 string, arg2 *job.DockerListData) (*job.Response, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryContainerList", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "QueryDockerList", arg0, arg1, arg2) ret0, _ := ret[0].(*job.Response) ret1, _ := ret[1].(error) return ret0, ret1 } -// QueryContainerList indicates an expected call of QueryContainerList. -func (mr *MockJobClientMockRecorder) QueryContainerList(arg0, arg1, arg2 interface{}) *gomock.Call { +// QueryDockerList indicates an expected call of QueryDockerList. +func (mr *MockJobClientMockRecorder) QueryDockerList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryContainerList", reflect.TypeOf((*MockJobClient)(nil).QueryContainerList), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryDockerList", reflect.TypeOf((*MockJobClient)(nil).QueryDockerList), arg0, arg1, arg2) } // QueryFileStatus mocks base method. diff --git a/internal/job/subjects.go b/internal/job/subjects.go index 0ee5e834..efa02800 100644 --- a/internal/job/subjects.go +++ b/internal/job/subjects.go @@ -85,9 +85,9 @@ const ( // Subject categories for different operations const ( - SubjectCategoryNode = "node" - SubjectCategoryNetwork = "network" - SubjectCategoryContainer = "container" + SubjectCategoryNode = "node" + SubjectCategoryNetwork = "network" + SubjectCategoryDocker = "docker" ) // Node operation types diff --git a/internal/job/types.go b/internal/job/types.go index 32088920..d81189a1 100644 --- a/internal/job/types.go +++ b/internal/job/types.go @@ -132,16 +132,16 @@ const ( OperationFileStatusGet = "file.status.get" ) -// Container operation types +// Docker operation types const ( - OperationContainerCreate = "container.create.execute" - OperationContainerStart = "container.start.execute" - OperationContainerStop = "container.stop.execute" - OperationContainerRemove = "container.remove.execute" - OperationContainerList = "container.list.get" - OperationContainerInspect = "container.inspect.get" - OperationContainerExec = "container.exec.execute" - OperationContainerPull = "container.pull.execute" + OperationDockerCreate = "docker.create.execute" + OperationDockerStart = "docker.start.execute" + OperationDockerStop = "docker.stop.execute" + OperationDockerRemove = "docker.remove.execute" + OperationDockerList = "docker.list.get" + OperationDockerInspect = "docker.inspect.get" + OperationDockerExec = "docker.exec.execute" + OperationDockerPull = "docker.pull.execute" ) // Operation represents an operation in the new hierarchical format @@ -260,8 +260,8 @@ type CommandShellData struct { Timeout int `json:"timeout,omitempty"` } -// ContainerCreateData represents data for container creation. -type ContainerCreateData struct { +// DockerCreateData represents data for docker container creation. +type DockerCreateData struct { Image string `json:"image"` Name string `json:"name,omitempty"` Command []string `json:"command,omitempty"` @@ -286,31 +286,31 @@ type VolumeMapping struct { Container string `json:"container"` } -// ContainerStopData represents data for stopping a container. -type ContainerStopData struct { +// DockerStopData represents data for stopping a docker container. +type DockerStopData struct { Timeout *int `json:"timeout,omitempty"` } -// ContainerRemoveData represents data for removing a container. -type ContainerRemoveData struct { +// DockerRemoveData represents data for removing a docker container. +type DockerRemoveData struct { Force bool `json:"force,omitempty"` } -// ContainerListData represents data for listing containers. -type ContainerListData struct { +// DockerListData represents data for listing docker containers. +type DockerListData struct { State string `json:"state,omitempty"` Limit int `json:"limit,omitempty"` } -// ContainerExecData represents data for executing a command in a container. -type ContainerExecData struct { +// DockerExecData represents data for executing a command in a docker container. +type DockerExecData struct { Command []string `json:"command"` Env map[string]string `json:"env,omitempty"` WorkingDir string `json:"working_dir,omitempty"` } -// ContainerPullData represents data for pulling an image. -type ContainerPullData struct { +// DockerPullData represents data for pulling a docker image. +type DockerPullData struct { Image string `json:"image"` } diff --git a/internal/provider/container/provider.go b/internal/provider/container/provider.go deleted file mode 100644 index 4a1f5780..00000000 --- a/internal/provider/container/provider.go +++ /dev/null @@ -1,103 +0,0 @@ -package container - -import ( - "context" - "time" - - "github.com/retr0h/osapi/internal/provider/container/runtime" -) - -// Service implements Provider by delegating to a runtime.Driver. -type Service struct { - driver runtime.Driver -} - -// New creates a new container provider service. -func New( - driver runtime.Driver, -) *Service { - return &Service{driver: driver} -} - -// Create delegates to the driver to create a container. -func ( - s *Service, -) Create( - ctx context.Context, - params runtime.CreateParams, -) (*runtime.Container, error) { - return s.driver.Create(ctx, params) -} - -// Start delegates to the driver to start a container. -func ( - s *Service, -) Start( - ctx context.Context, - id string, -) error { - return s.driver.Start(ctx, id) -} - -// Stop delegates to the driver to stop a container. -func ( - s *Service, -) Stop( - ctx context.Context, - id string, - timeout *time.Duration, -) error { - return s.driver.Stop(ctx, id, timeout) -} - -// Remove delegates to the driver to remove a container. -func ( - s *Service, -) Remove( - ctx context.Context, - id string, - force bool, -) error { - return s.driver.Remove(ctx, id, force) -} - -// List delegates to the driver to list containers. -func ( - s *Service, -) List( - ctx context.Context, - params runtime.ListParams, -) ([]runtime.Container, error) { - return s.driver.List(ctx, params) -} - -// Inspect delegates to the driver to inspect a container. -func ( - s *Service, -) Inspect( - ctx context.Context, - id string, -) (*runtime.ContainerDetail, error) { - return s.driver.Inspect(ctx, id) -} - -// Exec delegates to the driver to execute a command in a container. -func ( - s *Service, -) Exec( - ctx context.Context, - id string, - params runtime.ExecParams, -) (*runtime.ExecResult, error) { - return s.driver.Exec(ctx, id, params) -} - -// Pull delegates to the driver to pull a container image. -func ( - s *Service, -) Pull( - ctx context.Context, - image string, -) (*runtime.PullResult, error) { - return s.driver.Pull(ctx, image) -} diff --git a/internal/provider/container/provider_public_test.go b/internal/provider/container/provider_public_test.go deleted file mode 100644 index cae5f40b..00000000 --- a/internal/provider/container/provider_public_test.go +++ /dev/null @@ -1,544 +0,0 @@ -package container_test - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/internal/provider/container" - "github.com/retr0h/osapi/internal/provider/container/runtime" - runtimeMocks "github.com/retr0h/osapi/internal/provider/container/runtime/mocks" -) - -type ProviderPublicTestSuite struct { - suite.Suite - - mockCtrl *gomock.Controller - mockDriver *runtimeMocks.MockDriver - service *container.Service - ctx context.Context -} - -func (s *ProviderPublicTestSuite) SetupTest() { - s.mockCtrl = gomock.NewController(s.T()) - s.mockDriver = runtimeMocks.NewMockDriver(s.mockCtrl) - s.service = container.New(s.mockDriver) - s.ctx = context.Background() -} - -func (s *ProviderPublicTestSuite) TearDownTest() { - s.mockCtrl.Finish() -} - -func ( - s *ProviderPublicTestSuite, -) TestNew() { - tests := []struct { - name string - validateFunc func(p container.Provider) - }{ - { - name: "returns non-nil provider", - validateFunc: func(p container.Provider) { - s.NotNil(p) - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - var driver runtime.Driver // nil driver for unit test - p := container.New(driver) - tt.validateFunc(p) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestCreate() { - tests := []struct { - name string - params runtime.CreateParams - setupMock func() - validateFunc func(*runtime.Container, error) - }{ - { - name: "delegates to driver and returns result", - params: runtime.CreateParams{ - Image: "nginx:latest", - Name: "web", - }, - setupMock: func() { - s.mockDriver.EXPECT(). - Create(gomock.Any(), runtime.CreateParams{ - Image: "nginx:latest", - Name: "web", - }). - Return(&runtime.Container{ - ID: "abc123", - Name: "web", - Image: "nginx:latest", - State: "created", - }, nil) - }, - validateFunc: func( - c *runtime.Container, - err error, - ) { - s.NoError(err) - s.NotNil(c) - s.Equal("abc123", c.ID) - s.Equal("web", c.Name) - }, - }, - { - name: "returns error from driver", - params: runtime.CreateParams{ - Image: "invalid:image", - }, - setupMock: func() { - s.mockDriver.EXPECT(). - Create(gomock.Any(), gomock.Any()). - Return(nil, errors.New("image not found")) - }, - validateFunc: func( - c *runtime.Container, - err error, - ) { - s.Error(err) - s.Nil(c) - s.Contains(err.Error(), "image not found") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - c, err := s.service.Create(s.ctx, tt.params) - tt.validateFunc(c, err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestStart() { - tests := []struct { - name string - id string - setupMock func() - validateFunc func(error) - }{ - { - name: "delegates to driver and returns nil", - id: "abc123", - setupMock: func() { - s.mockDriver.EXPECT(). - Start(gomock.Any(), "abc123"). - Return(nil) - }, - validateFunc: func(err error) { - s.NoError(err) - }, - }, - { - name: "returns error from driver", - id: "abc123", - setupMock: func() { - s.mockDriver.EXPECT(). - Start(gomock.Any(), "abc123"). - Return(errors.New("container not found")) - }, - validateFunc: func(err error) { - s.Error(err) - s.Contains(err.Error(), "container not found") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - err := s.service.Start(s.ctx, tt.id) - tt.validateFunc(err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestStop() { - timeout := 10 * time.Second - tests := []struct { - name string - id string - timeout *time.Duration - setupMock func() - validateFunc func(error) - }{ - { - name: "delegates to driver with timeout", - id: "abc123", - timeout: &timeout, - setupMock: func() { - s.mockDriver.EXPECT(). - Stop(gomock.Any(), "abc123", &timeout). - Return(nil) - }, - validateFunc: func(err error) { - s.NoError(err) - }, - }, - { - name: "delegates to driver without timeout", - id: "abc123", - timeout: nil, - setupMock: func() { - s.mockDriver.EXPECT(). - Stop(gomock.Any(), "abc123", (*time.Duration)(nil)). - Return(nil) - }, - validateFunc: func(err error) { - s.NoError(err) - }, - }, - { - name: "returns error from driver", - id: "abc123", - timeout: &timeout, - setupMock: func() { - s.mockDriver.EXPECT(). - Stop(gomock.Any(), "abc123", gomock.Any()). - Return(errors.New("stop failed")) - }, - validateFunc: func(err error) { - s.Error(err) - s.Contains(err.Error(), "stop failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - err := s.service.Stop(s.ctx, tt.id, tt.timeout) - tt.validateFunc(err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestRemove() { - tests := []struct { - name string - id string - force bool - setupMock func() - validateFunc func(error) - }{ - { - name: "delegates to driver with force", - id: "abc123", - force: true, - setupMock: func() { - s.mockDriver.EXPECT(). - Remove(gomock.Any(), "abc123", true). - Return(nil) - }, - validateFunc: func(err error) { - s.NoError(err) - }, - }, - { - name: "delegates to driver without force", - id: "abc123", - force: false, - setupMock: func() { - s.mockDriver.EXPECT(). - Remove(gomock.Any(), "abc123", false). - Return(nil) - }, - validateFunc: func(err error) { - s.NoError(err) - }, - }, - { - name: "returns error from driver", - id: "abc123", - force: true, - setupMock: func() { - s.mockDriver.EXPECT(). - Remove(gomock.Any(), "abc123", true). - Return(errors.New("remove failed")) - }, - validateFunc: func(err error) { - s.Error(err) - s.Contains(err.Error(), "remove failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - err := s.service.Remove(s.ctx, tt.id, tt.force) - tt.validateFunc(err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestList() { - tests := []struct { - name string - params runtime.ListParams - setupMock func() - validateFunc func([]runtime.Container, error) - }{ - { - name: "delegates to driver and returns containers", - params: runtime.ListParams{ - State: "running", - Limit: 10, - }, - setupMock: func() { - s.mockDriver.EXPECT(). - List(gomock.Any(), runtime.ListParams{ - State: "running", - Limit: 10, - }). - Return([]runtime.Container{ - {ID: "abc123", Name: "web", State: "running"}, - }, nil) - }, - validateFunc: func( - containers []runtime.Container, - err error, - ) { - s.NoError(err) - s.Len(containers, 1) - s.Equal("abc123", containers[0].ID) - }, - }, - { - name: "returns error from driver", - params: runtime.ListParams{State: "all"}, - setupMock: func() { - s.mockDriver.EXPECT(). - List(gomock.Any(), gomock.Any()). - Return(nil, errors.New("list failed")) - }, - validateFunc: func( - containers []runtime.Container, - err error, - ) { - s.Error(err) - s.Nil(containers) - s.Contains(err.Error(), "list failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - containers, err := s.service.List(s.ctx, tt.params) - tt.validateFunc(containers, err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestInspect() { - tests := []struct { - name string - id string - setupMock func() - validateFunc func(*runtime.ContainerDetail, error) - }{ - { - name: "delegates to driver and returns detail", - id: "abc123", - setupMock: func() { - s.mockDriver.EXPECT(). - Inspect(gomock.Any(), "abc123"). - Return(&runtime.ContainerDetail{ - Container: runtime.Container{ID: "abc123"}, - }, nil) - }, - validateFunc: func( - detail *runtime.ContainerDetail, - err error, - ) { - s.NoError(err) - s.NotNil(detail) - s.Equal("abc123", detail.ID) - }, - }, - { - name: "returns error from driver", - id: "abc123", - setupMock: func() { - s.mockDriver.EXPECT(). - Inspect(gomock.Any(), "abc123"). - Return(nil, errors.New("inspect failed")) - }, - validateFunc: func( - detail *runtime.ContainerDetail, - err error, - ) { - s.Error(err) - s.Nil(detail) - s.Contains(err.Error(), "inspect failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - detail, err := s.service.Inspect(s.ctx, tt.id) - tt.validateFunc(detail, err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestExec() { - tests := []struct { - name string - id string - params runtime.ExecParams - setupMock func() - validateFunc func(*runtime.ExecResult, error) - }{ - { - name: "delegates to driver and returns result", - id: "abc123", - params: runtime.ExecParams{ - Command: []string{"ls", "-la"}, - }, - setupMock: func() { - s.mockDriver.EXPECT(). - Exec(gomock.Any(), "abc123", runtime.ExecParams{ - Command: []string{"ls", "-la"}, - }). - Return(&runtime.ExecResult{ - Stdout: "output", - ExitCode: 0, - }, nil) - }, - validateFunc: func( - result *runtime.ExecResult, - err error, - ) { - s.NoError(err) - s.NotNil(result) - s.Equal("output", result.Stdout) - s.Equal(0, result.ExitCode) - }, - }, - { - name: "returns error from driver", - id: "abc123", - params: runtime.ExecParams{ - Command: []string{"ls"}, - }, - setupMock: func() { - s.mockDriver.EXPECT(). - Exec(gomock.Any(), "abc123", gomock.Any()). - Return(nil, errors.New("exec failed")) - }, - validateFunc: func( - result *runtime.ExecResult, - err error, - ) { - s.Error(err) - s.Nil(result) - s.Contains(err.Error(), "exec failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - result, err := s.service.Exec(s.ctx, tt.id, tt.params) - tt.validateFunc(result, err) - }) - } -} - -func ( - s *ProviderPublicTestSuite, -) TestPull() { - tests := []struct { - name string - image string - setupMock func() - validateFunc func(*runtime.PullResult, error) - }{ - { - name: "delegates to driver and returns result", - image: "nginx:latest", - setupMock: func() { - s.mockDriver.EXPECT(). - Pull(gomock.Any(), "nginx:latest"). - Return(&runtime.PullResult{ - ImageID: "sha256:abc", - Tag: "latest", - Size: 2048, - }, nil) - }, - validateFunc: func( - result *runtime.PullResult, - err error, - ) { - s.NoError(err) - s.NotNil(result) - s.Equal("sha256:abc", result.ImageID) - s.Equal("latest", result.Tag) - s.Equal(int64(2048), result.Size) - }, - }, - { - name: "returns error from driver", - image: "invalid:image", - setupMock: func() { - s.mockDriver.EXPECT(). - Pull(gomock.Any(), "invalid:image"). - Return(nil, errors.New("pull failed")) - }, - validateFunc: func( - result *runtime.PullResult, - err error, - ) { - s.Error(err) - s.Nil(result) - s.Contains(err.Error(), "pull failed") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - tt.setupMock() - result, err := s.service.Pull(s.ctx, tt.image) - tt.validateFunc(result, err) - }) - } -} - -func TestProviderPublicTestSuite(t *testing.T) { - suite.Run(t, new(ProviderPublicTestSuite)) -} diff --git a/internal/provider/container/runtime/mocks/driver.gen.go b/internal/provider/container/runtime/mocks/driver.gen.go deleted file mode 100644 index e1793ff4..00000000 --- a/internal/provider/container/runtime/mocks/driver.gen.go +++ /dev/null @@ -1,168 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ../driver.go - -// Package mocks is a generated GoMock package. -package mocks - -import ( - context "context" - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" - runtime "github.com/retr0h/osapi/internal/provider/container/runtime" -) - -// MockDriver is a mock of Driver interface. -type MockDriver struct { - ctrl *gomock.Controller - recorder *MockDriverMockRecorder -} - -// MockDriverMockRecorder is the mock recorder for MockDriver. -type MockDriverMockRecorder struct { - mock *MockDriver -} - -// NewMockDriver creates a new mock instance. -func NewMockDriver(ctrl *gomock.Controller) *MockDriver { - mock := &MockDriver{ctrl: ctrl} - mock.recorder = &MockDriverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDriver) EXPECT() *MockDriverMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockDriver) Create(ctx context.Context, params runtime.CreateParams) (*runtime.Container, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", ctx, params) - ret0, _ := ret[0].(*runtime.Container) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create. -func (mr *MockDriverMockRecorder) Create(ctx, params interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDriver)(nil).Create), ctx, params) -} - -// Exec mocks base method. -func (m *MockDriver) Exec(ctx context.Context, id string, params runtime.ExecParams) (*runtime.ExecResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Exec", ctx, id, params) - ret0, _ := ret[0].(*runtime.ExecResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Exec indicates an expected call of Exec. -func (mr *MockDriverMockRecorder) Exec(ctx, id, params interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockDriver)(nil).Exec), ctx, id, params) -} - -// Inspect mocks base method. -func (m *MockDriver) Inspect(ctx context.Context, id string) (*runtime.ContainerDetail, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Inspect", ctx, id) - ret0, _ := ret[0].(*runtime.ContainerDetail) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Inspect indicates an expected call of Inspect. -func (mr *MockDriverMockRecorder) Inspect(ctx, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Inspect", reflect.TypeOf((*MockDriver)(nil).Inspect), ctx, id) -} - -// List mocks base method. -func (m *MockDriver) List(ctx context.Context, params runtime.ListParams) ([]runtime.Container, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", ctx, params) - ret0, _ := ret[0].([]runtime.Container) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockDriverMockRecorder) List(ctx, params interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockDriver)(nil).List), ctx, params) -} - -// Ping mocks base method. -func (m *MockDriver) Ping(ctx context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Ping", ctx) - ret0, _ := ret[0].(error) - return ret0 -} - -// Ping indicates an expected call of Ping. -func (mr *MockDriverMockRecorder) Ping(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockDriver)(nil).Ping), ctx) -} - -// Pull mocks base method. -func (m *MockDriver) Pull(ctx context.Context, image string) (*runtime.PullResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Pull", ctx, image) - ret0, _ := ret[0].(*runtime.PullResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Pull indicates an expected call of Pull. -func (mr *MockDriverMockRecorder) Pull(ctx, image interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pull", reflect.TypeOf((*MockDriver)(nil).Pull), ctx, image) -} - -// Remove mocks base method. -func (m *MockDriver) Remove(ctx context.Context, id string, force bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Remove", ctx, id, force) - ret0, _ := ret[0].(error) - return ret0 -} - -// Remove indicates an expected call of Remove. -func (mr *MockDriverMockRecorder) Remove(ctx, id, force interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockDriver)(nil).Remove), ctx, id, force) -} - -// Start mocks base method. -func (m *MockDriver) Start(ctx context.Context, id string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Start indicates an expected call of Start. -func (mr *MockDriverMockRecorder) Start(ctx, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockDriver)(nil).Start), ctx, id) -} - -// Stop mocks base method. -func (m *MockDriver) Stop(ctx context.Context, id string, timeout *time.Duration) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Stop", ctx, id, timeout) - ret0, _ := ret[0].(error) - return ret0 -} - -// Stop indicates an expected call of Stop. -func (mr *MockDriverMockRecorder) Stop(ctx, id, timeout interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockDriver)(nil).Stop), ctx, id, timeout) -} diff --git a/internal/provider/container/types.go b/internal/provider/container/types.go deleted file mode 100644 index b1833cff..00000000 --- a/internal/provider/container/types.go +++ /dev/null @@ -1,57 +0,0 @@ -// Package container provides the container management provider. -package container - -import ( - "context" - "time" - - "github.com/retr0h/osapi/internal/provider/container/runtime" -) - -// Provider defines the container management interface. -// All methods accept context.Context for cancellation and timeout propagation, -// which is important since the Docker daemon is a remote service. -type Provider interface { - Create( - ctx context.Context, - params runtime.CreateParams, - ) (*runtime.Container, error) - - Start( - ctx context.Context, - id string, - ) error - - Stop( - ctx context.Context, - id string, - timeout *time.Duration, - ) error - - Remove( - ctx context.Context, - id string, - force bool, - ) error - - List( - ctx context.Context, - params runtime.ListParams, - ) ([]runtime.Container, error) - - Inspect( - ctx context.Context, - id string, - ) (*runtime.ContainerDetail, error) - - Exec( - ctx context.Context, - id string, - params runtime.ExecParams, - ) (*runtime.ExecResult, error) - - Pull( - ctx context.Context, - image string, - ) (*runtime.PullResult, error) -} diff --git a/internal/provider/container/runtime/docker/docker.go b/internal/provider/docker/docker.go similarity index 81% rename from internal/provider/container/runtime/docker/docker.go rename to internal/provider/docker/docker.go index 4afda96a..5e66128e 100644 --- a/internal/provider/container/runtime/docker/docker.go +++ b/internal/provider/docker/docker.go @@ -1,4 +1,4 @@ -// Package docker implements the runtime.Driver interface using the Docker Engine API. +// Package docker provides the Docker container management provider using the Docker Engine API. package docker import ( @@ -18,17 +18,15 @@ import ( "github.com/docker/docker/api/types/network" dockerclient "github.com/docker/docker/client" "github.com/docker/go-connections/nat" - - "github.com/retr0h/osapi/internal/provider/container/runtime" ) -// Driver implements runtime.Driver using the Docker Engine API. -type Driver struct { +// Client implements Driver using the Docker Engine API. +type Client struct { client dockerclient.APIClient } -// New creates a new Docker driver using default client options. -func New() (*Driver, error) { +// New creates a new Docker provider using default client options. +func New() (*Client, error) { cli, err := dockerclient.NewClientWithOpts( dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation(), @@ -37,18 +35,18 @@ func New() (*Driver, error) { return nil, fmt.Errorf("create docker client: %w", err) } - return &Driver{client: cli}, nil + return &Client{client: cli}, nil } -// NewWithClient creates a Docker driver with an injected client (for testing). +// NewWithClient creates a Docker provider with an injected client (for testing). func NewWithClient( client dockerclient.APIClient, -) *Driver { - return &Driver{client: client} +) *Client { + return &Client{client: client} } // Ping verifies connectivity to the Docker daemon. -func (d *Driver) Ping( +func (d *Client) Ping( ctx context.Context, ) error { _, err := d.client.Ping(ctx) @@ -60,10 +58,10 @@ func (d *Driver) Ping( } // Create creates a new container from the given parameters. -func (d *Driver) Create( +func (d *Client) Create( ctx context.Context, - params runtime.CreateParams, -) (*runtime.Container, error) { + params CreateParams, +) (*Container, error) { // Build Docker container configuration config := &container.Config{ Image: params.Image, @@ -131,39 +129,43 @@ func (d *Driver) Create( // Start the container if AutoStart is enabled if params.AutoStart { - if err := d.Start(ctx, resp.ID); err != nil { + if _, err := d.Start(ctx, resp.ID); err != nil { return nil, fmt.Errorf("auto-start container: %w", err) } } // Return container summary - return &runtime.Container{ + return &Container{ ID: resp.ID, Name: params.Name, Image: params.Image, State: "created", Created: time.Now(), + Changed: true, }, nil } // Start starts a stopped container. -func (d *Driver) Start( +func (d *Client) Start( ctx context.Context, id string, -) error { +) (*ActionResult, error) { if err := d.client.ContainerStart(ctx, id, container.StartOptions{}); err != nil { - return fmt.Errorf("start container: %w", err) + return nil, fmt.Errorf("start container: %w", err) } - return nil + return &ActionResult{ + Message: "Container started successfully", + Changed: true, + }, nil } // Stop stops a running container with an optional timeout. -func (d *Driver) Stop( +func (d *Client) Stop( ctx context.Context, id string, timeout *time.Duration, -) error { +) (*ActionResult, error) { opts := container.StopOptions{} if timeout != nil { seconds := int(timeout.Seconds()) @@ -171,34 +173,40 @@ func (d *Driver) Stop( } if err := d.client.ContainerStop(ctx, id, opts); err != nil { - return fmt.Errorf("stop container: %w", err) + return nil, fmt.Errorf("stop container: %w", err) } - return nil + return &ActionResult{ + Message: "Container stopped successfully", + Changed: true, + }, nil } // Remove removes a container. -func (d *Driver) Remove( +func (d *Client) Remove( ctx context.Context, id string, force bool, -) error { +) (*ActionResult, error) { opts := container.RemoveOptions{ Force: force, } if err := d.client.ContainerRemove(ctx, id, opts); err != nil { - return fmt.Errorf("remove container: %w", err) + return nil, fmt.Errorf("remove container: %w", err) } - return nil + return &ActionResult{ + Message: "Container removed successfully", + Changed: true, + }, nil } // List returns a list of containers matching the given parameters. -func (d *Driver) List( +func (d *Client) List( ctx context.Context, - params runtime.ListParams, -) ([]runtime.Container, error) { + params ListParams, +) ([]Container, error) { opts := container.ListOptions{} // Apply state filter @@ -224,8 +232,8 @@ func (d *Driver) List( return nil, fmt.Errorf("list containers: %w", err) } - // Convert to runtime.Container - result := make([]runtime.Container, 0, len(containers)) + // Convert to Container + result := make([]Container, 0, len(containers)) for _, c := range containers { name := "" if len(c.Names) > 0 { @@ -233,7 +241,7 @@ func (d *Driver) List( name = strings.TrimPrefix(c.Names[0], "/") } - result = append(result, runtime.Container{ + result = append(result, Container{ ID: c.ID, Name: name, Image: c.Image, @@ -246,10 +254,10 @@ func (d *Driver) List( } // Inspect returns detailed information about a container. -func (d *Driver) Inspect( +func (d *Client) Inspect( ctx context.Context, id string, -) (*runtime.ContainerDetail, error) { +) (*ContainerDetail, error) { resp, err := d.client.ContainerInspect(ctx, id) if err != nil { return nil, fmt.Errorf("inspect container: %w", err) @@ -268,8 +276,8 @@ func (d *Driver) Inspect( created = time.Now() } - detail := &runtime.ContainerDetail{ - Container: runtime.Container{ + detail := &ContainerDetail{ + Container: Container{ ID: resp.ID, Name: name, Image: resp.Config.Image, @@ -281,7 +289,7 @@ func (d *Driver) Inspect( // Add network settings if resp.NetworkSettings != nil { for _, netConfig := range resp.NetworkSettings.Networks { - detail.NetworkSettings = &runtime.NetworkSettings{ + detail.NetworkSettings = &NetworkSettings{ IPAddress: netConfig.IPAddress, Gateway: netConfig.Gateway, } @@ -291,12 +299,12 @@ func (d *Driver) Inspect( // Add port mappings if resp.HostConfig != nil && resp.HostConfig.PortBindings != nil { - ports := make([]runtime.PortMapping, 0) + ports := make([]PortMapping, 0) for containerPort, bindings := range resp.HostConfig.PortBindings { for _, binding := range bindings { hostPort, _ := strconv.Atoi(binding.HostPort) cPort, _ := strconv.Atoi(containerPort.Port()) - ports = append(ports, runtime.PortMapping{ + ports = append(ports, PortMapping{ Host: hostPort, Container: cPort, }) @@ -307,9 +315,9 @@ func (d *Driver) Inspect( // Add mounts if len(resp.Mounts) > 0 { - mounts := make([]runtime.VolumeMapping, 0, len(resp.Mounts)) + mounts := make([]VolumeMapping, 0, len(resp.Mounts)) for _, m := range resp.Mounts { - mounts = append(mounts, runtime.VolumeMapping{ + mounts = append(mounts, VolumeMapping{ Host: m.Source, Container: m.Destination, }) @@ -326,11 +334,11 @@ func (d *Driver) Inspect( } // Exec executes a command in a running container. -func (d *Driver) Exec( +func (d *Client) Exec( ctx context.Context, id string, - params runtime.ExecParams, -) (*runtime.ExecResult, error) { + params ExecParams, +) (*ExecResult, error) { // Create exec configuration execConfig := container.ExecOptions{ Cmd: params.Command, @@ -375,18 +383,19 @@ func (d *Driver) Exec( return nil, fmt.Errorf("inspect exec: %w", err) } - return &runtime.ExecResult{ + return &ExecResult{ Stdout: stdout.String(), Stderr: stderr.String(), ExitCode: inspectResp.ExitCode, + Changed: true, }, nil } // Pull pulls a container image from a registry. -func (d *Driver) Pull( +func (d *Client) Pull( ctx context.Context, imageName string, -) (*runtime.PullResult, error) { +) (*PullResult, error) { pullResp, err := d.client.ImagePull(ctx, imageName, image.PullOptions{}) if err != nil { return nil, fmt.Errorf("pull image: %w", err) @@ -422,10 +431,11 @@ func (d *Driver) Pull( } } - result := &runtime.PullResult{ + result := &PullResult{ ImageID: inspectResp.ID, Tag: tag, Size: inspectResp.Size, + Changed: true, } // Extract digest from last event if available diff --git a/internal/provider/container/runtime/docker/docker_public_test.go b/internal/provider/docker/docker_public_test.go similarity index 91% rename from internal/provider/container/runtime/docker/docker_public_test.go rename to internal/provider/docker/docker_public_test.go index 74525ae7..6806de4c 100644 --- a/internal/provider/container/runtime/docker/docker_public_test.go +++ b/internal/provider/docker/docker_public_test.go @@ -21,8 +21,7 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/suite" - "github.com/retr0h/osapi/internal/provider/container/runtime" - "github.com/retr0h/osapi/internal/provider/container/runtime/docker" + dockerprov "github.com/retr0h/osapi/internal/provider/docker" ) // mockDockerClient embeds dockerclient.APIClient and overrides specific methods for testing. @@ -259,12 +258,12 @@ func (s *DockerDriverPublicTestSuite) TestNew() { tests := []struct { name string setupEnv func() (cleanup func()) - validateFunc func(d runtime.Driver, err error) + validateFunc func(d dockerprov.Provider, err error) }{ { name: "returns non-nil driver", validateFunc: func( - d runtime.Driver, + d dockerprov.Provider, err error, ) { s.NoError(err) @@ -286,7 +285,7 @@ func (s *DockerDriverPublicTestSuite) TestNew() { } }, validateFunc: func( - d runtime.Driver, + d dockerprov.Provider, err error, ) { s.Error(err) @@ -303,7 +302,7 @@ func (s *DockerDriverPublicTestSuite) TestNew() { defer cleanup() } - d, err := docker.New() + d, err := dockerprov.New() tt.validateFunc(d, err) }) } @@ -350,7 +349,7 @@ func (s *DockerDriverPublicTestSuite) TestPing() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) err := d.Ping(s.ctx) tt.validateFunc(err) }) @@ -361,8 +360,8 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { tests := []struct { name string mockClient *mockDockerClient - params runtime.CreateParams - validateFunc func(c *runtime.Container, err error) + params dockerprov.CreateParams + validateFunc func(c *dockerprov.Container, err error) }{ { name: "successful container creation", @@ -378,12 +377,12 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{ID: "test-id"}, nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nginx:latest", Name: "test-nginx", }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -412,13 +411,13 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nginx:latest", Name: "test-nginx-auto", AutoStart: true, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -442,13 +441,13 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{ID: "cmd-id"}, nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "alpine:latest", Name: "test-cmd", Command: []string{"echo", "hello"}, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -473,13 +472,13 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{ID: "env-id"}, nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "alpine:latest", Name: "test-env", Env: map[string]string{"FOO": "bar"}, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -507,15 +506,15 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{ID: "ports-id"}, nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nginx:latest", Name: "test-ports", - Ports: []runtime.PortMapping{ + Ports: []dockerprov.PortMapping{ {Host: 8080, Container: 80}, }, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -541,15 +540,15 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{ID: "vols-id"}, nil }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nginx:latest", Name: "test-vols", - Volumes: []runtime.VolumeMapping{ + Volumes: []dockerprov.VolumeMapping{ {Host: "/host/data", Container: "/container/data"}, }, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.NoError(err) @@ -571,12 +570,12 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return container.CreateResponse{}, fmt.Errorf("image not found") }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nonexistent:latest", Name: "test-fail", }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.Error(err) @@ -605,13 +604,13 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { return fmt.Errorf("start failed") }, }, - params: runtime.CreateParams{ + params: dockerprov.CreateParams{ Image: "nginx:latest", Name: "test-autostart-fail", AutoStart: true, }, validateFunc: func( - c *runtime.Container, + c *dockerprov.Container, err error, ) { s.Error(err) @@ -623,7 +622,7 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) c, err := d.Create(s.ctx, tt.params) tt.validateFunc(c, err) }) @@ -678,8 +677,8 @@ func (s *DockerDriverPublicTestSuite) TestStart() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) - err := d.Start(s.ctx, tt.containerID) + d := dockerprov.NewWithClient(tt.mockClient) + _, err := d.Start(s.ctx, tt.containerID) tt.validateFunc(err) }) } @@ -760,8 +759,8 @@ func (s *DockerDriverPublicTestSuite) TestStop() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) - err := d.Stop(s.ctx, tt.containerID, tt.timeout) + d := dockerprov.NewWithClient(tt.mockClient) + _, err := d.Stop(s.ctx, tt.containerID, tt.timeout) tt.validateFunc(err) }) } @@ -818,8 +817,8 @@ func (s *DockerDriverPublicTestSuite) TestRemove() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) - err := d.Remove(s.ctx, tt.containerID, tt.force) + d := dockerprov.NewWithClient(tt.mockClient) + _, err := d.Remove(s.ctx, tt.containerID, tt.force) tt.validateFunc(err) }) } @@ -829,8 +828,8 @@ func (s *DockerDriverPublicTestSuite) TestList() { tests := []struct { name string mockClient *mockDockerClient - params runtime.ListParams - validateFunc func(containers []runtime.Container, err error) + params dockerprov.ListParams + validateFunc func(containers []dockerprov.Container, err error) }{ { name: "successful list all containers", @@ -852,9 +851,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { }, nil }, }, - params: runtime.ListParams{State: "all"}, + params: dockerprov.ListParams{State: "all"}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -874,9 +873,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { return []container.Summary{}, nil }, }, - params: runtime.ListParams{State: "running"}, + params: dockerprov.ListParams{State: "running"}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -896,9 +895,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { return []container.Summary{}, nil }, }, - params: runtime.ListParams{State: "stopped"}, + params: dockerprov.ListParams{State: "stopped"}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -917,9 +916,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { return []container.Summary{}, nil }, }, - params: runtime.ListParams{State: ""}, + params: dockerprov.ListParams{State: ""}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -938,9 +937,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { return []container.Summary{}, nil }, }, - params: runtime.ListParams{State: "all", Limit: 5}, + params: dockerprov.ListParams{State: "all", Limit: 5}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -965,9 +964,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { }, nil }, }, - params: runtime.ListParams{State: "all"}, + params: dockerprov.ListParams{State: "all"}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.NoError(err) @@ -985,9 +984,9 @@ func (s *DockerDriverPublicTestSuite) TestList() { return nil, fmt.Errorf("daemon error") }, }, - params: runtime.ListParams{State: "all"}, + params: dockerprov.ListParams{State: "all"}, validateFunc: func( - containers []runtime.Container, + containers []dockerprov.Container, err error, ) { s.Error(err) @@ -999,7 +998,7 @@ func (s *DockerDriverPublicTestSuite) TestList() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) containers, err := d.List(s.ctx, tt.params) tt.validateFunc(containers, err) }) @@ -1011,7 +1010,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { name string mockClient *mockDockerClient containerID string - validateFunc func(detail *runtime.ContainerDetail, err error) + validateFunc func(detail *dockerprov.ContainerDetail, err error) }{ { name: "successful inspect", @@ -1033,7 +1032,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "test-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1062,7 +1061,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "nil-state-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1090,7 +1089,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "bad-time-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1126,7 +1125,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "net-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1165,7 +1164,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "ports-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1201,7 +1200,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "mounts-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1236,7 +1235,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "health-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.NoError(err) @@ -1256,7 +1255,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { }, containerID: "missing-id", validateFunc: func( - detail *runtime.ContainerDetail, + detail *dockerprov.ContainerDetail, err error, ) { s.Error(err) @@ -1268,7 +1267,7 @@ func (s *DockerDriverPublicTestSuite) TestInspect() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) detail, err := d.Inspect(s.ctx, tt.containerID) tt.validateFunc(detail, err) }) @@ -1280,8 +1279,8 @@ func (s *DockerDriverPublicTestSuite) TestExec() { name string mockClient *mockDockerClient containerID string - params runtime.ExecParams - validateFunc func(result *runtime.ExecResult, err error) + params dockerprov.ExecParams + validateFunc func(result *dockerprov.ExecResult, err error) }{ { name: "successful exec", @@ -1312,11 +1311,11 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "test-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"echo", "hello"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.NoError(err) @@ -1353,12 +1352,12 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "test-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"env"}, Env: map[string]string{"MY_VAR": "value"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.NoError(err) @@ -1392,12 +1391,12 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "test-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"pwd"}, WorkingDir: "/app", }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.NoError(err) @@ -1416,11 +1415,11 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "stopped-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"ls"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.Error(err) @@ -1447,11 +1446,11 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "test-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"ls"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.Error(err) @@ -1478,11 +1477,11 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "container-1", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"ls"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.Error(err) @@ -1515,11 +1514,11 @@ func (s *DockerDriverPublicTestSuite) TestExec() { }, }, containerID: "test-id", - params: runtime.ExecParams{ + params: dockerprov.ExecParams{ Command: []string{"ls"}, }, validateFunc: func( - result *runtime.ExecResult, + result *dockerprov.ExecResult, err error, ) { s.Error(err) @@ -1531,7 +1530,7 @@ func (s *DockerDriverPublicTestSuite) TestExec() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) result, err := d.Exec(s.ctx, tt.containerID, tt.params) tt.validateFunc(result, err) }) @@ -1543,7 +1542,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { name string mockClient *mockDockerClient imageName string - validateFunc func(result *runtime.PullResult, err error) + validateFunc func(result *dockerprov.PullResult, err error) }{ { name: "successful image pull", @@ -1569,7 +1568,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "nginx:latest", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.NoError(err) @@ -1606,7 +1605,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "nginx:1.25", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.NoError(err) @@ -1628,7 +1627,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "nonexistent:latest", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.Error(err) @@ -1650,7 +1649,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "nginx:latest", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.Error(err) @@ -1682,7 +1681,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "custom-image", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.NoError(err) @@ -1710,7 +1709,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { }, imageName: "nginx:latest", validateFunc: func( - result *runtime.PullResult, + result *dockerprov.PullResult, err error, ) { s.Error(err) @@ -1722,7 +1721,7 @@ func (s *DockerDriverPublicTestSuite) TestPull() { for _, tt := range tests { s.Run(tt.name, func() { - d := docker.NewWithClient(tt.mockClient) + d := dockerprov.NewWithClient(tt.mockClient) result, err := d.Pull(s.ctx, tt.imageName) tt.validateFunc(result, err) }) diff --git a/internal/provider/container/mocks/generate.go b/internal/provider/docker/mocks/generate.go similarity index 100% rename from internal/provider/container/mocks/generate.go rename to internal/provider/docker/mocks/generate.go diff --git a/internal/provider/container/mocks/types.gen.go b/internal/provider/docker/mocks/types.gen.go similarity index 75% rename from internal/provider/container/mocks/types.gen.go rename to internal/provider/docker/mocks/types.gen.go index abb2ca60..33c658ac 100644 --- a/internal/provider/container/mocks/types.gen.go +++ b/internal/provider/docker/mocks/types.gen.go @@ -10,7 +10,7 @@ import ( time "time" gomock "github.com/golang/mock/gomock" - runtime "github.com/retr0h/osapi/internal/provider/container/runtime" + docker "github.com/retr0h/osapi/internal/provider/docker" ) // MockProvider is a mock of Provider interface. @@ -37,10 +37,10 @@ func (m *MockProvider) EXPECT() *MockProviderMockRecorder { } // Create mocks base method. -func (m *MockProvider) Create(ctx context.Context, params runtime.CreateParams) (*runtime.Container, error) { +func (m *MockProvider) Create(ctx context.Context, params docker.CreateParams) (*docker.Container, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Create", ctx, params) - ret0, _ := ret[0].(*runtime.Container) + ret0, _ := ret[0].(*docker.Container) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -52,10 +52,10 @@ func (mr *MockProviderMockRecorder) Create(ctx, params interface{}) *gomock.Call } // Exec mocks base method. -func (m *MockProvider) Exec(ctx context.Context, id string, params runtime.ExecParams) (*runtime.ExecResult, error) { +func (m *MockProvider) Exec(ctx context.Context, id string, params docker.ExecParams) (*docker.ExecResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Exec", ctx, id, params) - ret0, _ := ret[0].(*runtime.ExecResult) + ret0, _ := ret[0].(*docker.ExecResult) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -67,10 +67,10 @@ func (mr *MockProviderMockRecorder) Exec(ctx, id, params interface{}) *gomock.Ca } // Inspect mocks base method. -func (m *MockProvider) Inspect(ctx context.Context, id string) (*runtime.ContainerDetail, error) { +func (m *MockProvider) Inspect(ctx context.Context, id string) (*docker.ContainerDetail, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Inspect", ctx, id) - ret0, _ := ret[0].(*runtime.ContainerDetail) + ret0, _ := ret[0].(*docker.ContainerDetail) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -82,10 +82,10 @@ func (mr *MockProviderMockRecorder) Inspect(ctx, id interface{}) *gomock.Call { } // List mocks base method. -func (m *MockProvider) List(ctx context.Context, params runtime.ListParams) ([]runtime.Container, error) { +func (m *MockProvider) List(ctx context.Context, params docker.ListParams) ([]docker.Container, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "List", ctx, params) - ret0, _ := ret[0].([]runtime.Container) + ret0, _ := ret[0].([]docker.Container) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -96,11 +96,25 @@ func (mr *MockProviderMockRecorder) List(ctx, params interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockProvider)(nil).List), ctx, params) } +// Ping mocks base method. +func (m *MockProvider) Ping(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ping", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Ping indicates an expected call of Ping. +func (mr *MockProviderMockRecorder) Ping(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockProvider)(nil).Ping), ctx) +} + // Pull mocks base method. -func (m *MockProvider) Pull(ctx context.Context, image string) (*runtime.PullResult, error) { +func (m *MockProvider) Pull(ctx context.Context, image string) (*docker.PullResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Pull", ctx, image) - ret0, _ := ret[0].(*runtime.PullResult) + ret0, _ := ret[0].(*docker.PullResult) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -112,11 +126,12 @@ func (mr *MockProviderMockRecorder) Pull(ctx, image interface{}) *gomock.Call { } // Remove mocks base method. -func (m *MockProvider) Remove(ctx context.Context, id string, force bool) error { +func (m *MockProvider) Remove(ctx context.Context, id string, force bool) (*docker.ActionResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Remove", ctx, id, force) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(*docker.ActionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 } // Remove indicates an expected call of Remove. @@ -126,11 +141,12 @@ func (mr *MockProviderMockRecorder) Remove(ctx, id, force interface{}) *gomock.C } // Start mocks base method. -func (m *MockProvider) Start(ctx context.Context, id string) error { +func (m *MockProvider) Start(ctx context.Context, id string) (*docker.ActionResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", ctx, id) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(*docker.ActionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 } // Start indicates an expected call of Start. @@ -140,11 +156,12 @@ func (mr *MockProviderMockRecorder) Start(ctx, id interface{}) *gomock.Call { } // Stop mocks base method. -func (m *MockProvider) Stop(ctx context.Context, id string, timeout *time.Duration) error { +func (m *MockProvider) Stop(ctx context.Context, id string, timeout *time.Duration) (*docker.ActionResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stop", ctx, id, timeout) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(*docker.ActionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 } // Stop indicates an expected call of Stop. diff --git a/internal/provider/container/runtime/driver.go b/internal/provider/docker/types.go similarity index 83% rename from internal/provider/container/runtime/driver.go rename to internal/provider/docker/types.go index 6df455c8..17f6f281 100644 --- a/internal/provider/container/runtime/driver.go +++ b/internal/provider/docker/types.go @@ -1,14 +1,15 @@ -// Package runtime defines the container runtime driver interface. -package runtime +// Package docker provides the Docker container management provider. +package docker import ( "context" "time" ) -// Driver defines container runtime operations. -// Implementations: Docker (now), LXD/Podman (later). -type Driver interface { +// Provider defines the Docker container management interface. +// All methods accept context.Context for cancellation and timeout propagation, +// which is important since the Docker daemon is a remote service. +type Provider interface { Ping( ctx context.Context, ) error @@ -21,19 +22,19 @@ type Driver interface { Start( ctx context.Context, id string, - ) error + ) (*ActionResult, error) Stop( ctx context.Context, id string, timeout *time.Duration, - ) error + ) (*ActionResult, error) Remove( ctx context.Context, id string, force bool, - ) error + ) (*ActionResult, error) List( ctx context.Context, @@ -102,6 +103,13 @@ type Container struct { Image string `json:"image"` State string `json:"state"` Created time.Time `json:"created"` + Changed bool `json:"changed"` +} + +// ActionResult holds the result of a lifecycle action (start, stop, remove). +type ActionResult struct { + Message string `json:"message"` + Changed bool `json:"changed"` } // ContainerDetail holds detailed info for a container. @@ -134,6 +142,7 @@ type ExecResult struct { Stdout string `json:"stdout"` Stderr string `json:"stderr"` ExitCode int `json:"exit_code"` + Changed bool `json:"changed"` } // PullResult contains the result of an image pull. @@ -141,4 +150,5 @@ type PullResult struct { ImageID string `json:"image_id"` Tag string `json:"tag"` Size int64 `json:"size"` + Changed bool `json:"changed"` } diff --git a/internal/provider/registry/registry.go b/internal/provider/registry/registry.go deleted file mode 100644 index f9a05f3f..00000000 --- a/internal/provider/registry/registry.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -// Package registry provides a runtime registry for provider operations. -package registry - -import "context" - -// OperationSpec defines how to create params and run an operation. -type OperationSpec struct { - NewParams func() any - Run func(ctx context.Context, params any) (any, error) -} - -// Registration describes a provider and its operations. -type Registration struct { - Name string - Operations map[string]OperationSpec -} - -// Registry holds provider registrations. -type Registry struct { - providers map[string]Registration -} - -// New creates a new empty registry. -func New() *Registry { - return &Registry{ - providers: make(map[string]Registration), - } -} - -// Register adds a provider registration. -func (r *Registry) Register( - reg Registration, -) { - r.providers[reg.Name] = reg -} - -// Lookup finds an operation spec by provider and operation name. -func (r *Registry) Lookup( - provider string, - operation string, -) (*OperationSpec, bool) { - reg, ok := r.providers[provider] - if !ok { - return nil, false - } - - spec, ok := reg.Operations[operation] - if !ok { - return nil, false - } - - return &spec, true -} diff --git a/internal/provider/registry/registry_public_test.go b/internal/provider/registry/registry_public_test.go deleted file mode 100644 index 4befa466..00000000 --- a/internal/provider/registry/registry_public_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package registry_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/internal/provider/registry" -) - -type RegistryPublicTestSuite struct { - suite.Suite -} - -func (suite *RegistryPublicTestSuite) SetupTest() {} - -func (suite *RegistryPublicTestSuite) TearDownTest() {} - -func (suite *RegistryPublicTestSuite) TestLookup() { - tests := []struct { - name string - register bool - provider string - operation string - validateFunc func(spec *registry.OperationSpec, found bool) - }{ - { - name: "when registered provider found", - register: true, - provider: "container", - operation: "create", - validateFunc: func(spec *registry.OperationSpec, found bool) { - suite.True(found) - suite.NotNil(spec) - suite.NotNil(spec.NewParams) - suite.NotNil(spec.Run) - }, - }, - { - name: "when unregistered provider not found", - register: false, - provider: "nonexistent", - operation: "create", - validateFunc: func(spec *registry.OperationSpec, found bool) { - suite.False(found) - suite.Nil(spec) - }, - }, - { - name: "when registered provider wrong operation", - register: true, - provider: "container", - operation: "nonexistent", - validateFunc: func(spec *registry.OperationSpec, found bool) { - suite.False(found) - suite.Nil(spec) - }, - }, - { - name: "when run returns expected result", - register: true, - provider: "container", - operation: "create", - validateFunc: func(spec *registry.OperationSpec, found bool) { - suite.Require().True(found) - suite.Require().NotNil(spec) - - params := spec.NewParams() - suite.Equal("default-params", params) - - result, err := spec.Run(context.Background(), params) - suite.NoError(err) - suite.Equal("created", result) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - r := registry.New() - - if tc.register { - r.Register(registry.Registration{ - Name: "container", - Operations: map[string]registry.OperationSpec{ - "create": { - NewParams: func() any { - return "default-params" - }, - Run: func( - _ context.Context, - _ any, - ) (any, error) { - return "created", nil - }, - }, - }, - }) - } - - spec, found := r.Lookup(tc.provider, tc.operation) - tc.validateFunc(spec, found) - }) - } -} - -// In order for `go test` to run this suite, we need to create -// a normal test function and pass our suite to suite.Run. -func TestRegistryPublicTestSuite(t *testing.T) { - suite.Run(t, new(RegistryPublicTestSuite)) -} diff --git a/pkg/sdk/client/container.go b/pkg/sdk/client/docker.go similarity index 63% rename from pkg/sdk/client/container.go rename to pkg/sdk/client/docker.go index 85981c4e..5e8cc585 100644 --- a/pkg/sdk/client/container.go +++ b/pkg/sdk/client/docker.go @@ -27,20 +27,20 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// ContainerService provides container management operations. -type ContainerService struct { +// DockerService provides Docker container management operations. +type DockerService struct { client *gen.ClientWithResponses } // Create creates a new container on the target host. -func (s *ContainerService) Create( +func (s *DockerService) Create( ctx context.Context, hostname string, - body gen.ContainerCreateRequest, -) (*Response[Collection[ContainerResult]], error) { - resp, err := s.client.PostNodeContainerWithResponse(ctx, hostname, body) + body gen.DockerCreateRequest, +) (*Response[Collection[DockerResult]], error) { + resp, err := s.client.PostNodeContainerDockerWithResponse(ctx, hostname, body) if err != nil { - return nil, fmt.Errorf("create container: %w", err) + return nil, fmt.Errorf("docker create: %w", err) } if err := checkError( @@ -60,18 +60,18 @@ func (s *ContainerService) Create( }} } - return NewResponse(containerResultCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerResultCollectionFromGen(resp.JSON202), resp.Body), nil } // List lists containers on the target host, optionally filtered by state. -func (s *ContainerService) List( +func (s *DockerService) List( ctx context.Context, hostname string, - params *gen.GetNodeContainerParams, -) (*Response[Collection[ContainerListResult]], error) { - resp, err := s.client.GetNodeContainerWithResponse(ctx, hostname, params) + params *gen.GetNodeContainerDockerParams, +) (*Response[Collection[DockerListResult]], error) { + resp, err := s.client.GetNodeContainerDockerWithResponse(ctx, hostname, params) if err != nil { - return nil, fmt.Errorf("list containers: %w", err) + return nil, fmt.Errorf("docker list: %w", err) } if err := checkError( @@ -91,18 +91,18 @@ func (s *ContainerService) List( }} } - return NewResponse(containerListCollectionFromGen(resp.JSON200), resp.Body), nil + return NewResponse(dockerListCollectionFromGen(resp.JSON200), resp.Body), nil } // Inspect retrieves detailed information about a specific container. -func (s *ContainerService) Inspect( +func (s *DockerService) Inspect( ctx context.Context, hostname string, id string, -) (*Response[Collection[ContainerDetailResult]], error) { - resp, err := s.client.GetNodeContainerByIDWithResponse(ctx, hostname, id) +) (*Response[Collection[DockerDetailResult]], error) { + resp, err := s.client.GetNodeContainerDockerByIDWithResponse(ctx, hostname, id) if err != nil { - return nil, fmt.Errorf("inspect container: %w", err) + return nil, fmt.Errorf("docker inspect: %w", err) } if err := checkError( @@ -123,18 +123,18 @@ func (s *ContainerService) Inspect( }} } - return NewResponse(containerDetailCollectionFromGen(resp.JSON200), resp.Body), nil + return NewResponse(dockerDetailCollectionFromGen(resp.JSON200), resp.Body), nil } // Start starts a stopped container on the target host. -func (s *ContainerService) Start( +func (s *DockerService) Start( ctx context.Context, hostname string, id string, -) (*Response[Collection[ContainerActionResult]], error) { - resp, err := s.client.PostNodeContainerStartWithResponse(ctx, hostname, id) +) (*Response[Collection[DockerActionResult]], error) { + resp, err := s.client.PostNodeContainerDockerStartWithResponse(ctx, hostname, id) if err != nil { - return nil, fmt.Errorf("start container: %w", err) + return nil, fmt.Errorf("docker start: %w", err) } if err := checkError( @@ -155,19 +155,19 @@ func (s *ContainerService) Start( }} } - return NewResponse(containerActionCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerActionCollectionFromGen(resp.JSON202), resp.Body), nil } // Stop stops a running container on the target host. -func (s *ContainerService) Stop( +func (s *DockerService) Stop( ctx context.Context, hostname string, id string, - body gen.ContainerStopRequest, -) (*Response[Collection[ContainerActionResult]], error) { - resp, err := s.client.PostNodeContainerStopWithResponse(ctx, hostname, id, body) + body gen.DockerStopRequest, +) (*Response[Collection[DockerActionResult]], error) { + resp, err := s.client.PostNodeContainerDockerStopWithResponse(ctx, hostname, id, body) if err != nil { - return nil, fmt.Errorf("stop container: %w", err) + return nil, fmt.Errorf("docker stop: %w", err) } if err := checkError( @@ -188,19 +188,19 @@ func (s *ContainerService) Stop( }} } - return NewResponse(containerActionCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerActionCollectionFromGen(resp.JSON202), resp.Body), nil } // Remove removes a container from the target host. -func (s *ContainerService) Remove( +func (s *DockerService) Remove( ctx context.Context, hostname string, id string, - params *gen.DeleteNodeContainerByIDParams, -) (*Response[Collection[ContainerActionResult]], error) { - resp, err := s.client.DeleteNodeContainerByIDWithResponse(ctx, hostname, id, params) + params *gen.DeleteNodeContainerDockerByIDParams, +) (*Response[Collection[DockerActionResult]], error) { + resp, err := s.client.DeleteNodeContainerDockerByIDWithResponse(ctx, hostname, id, params) if err != nil { - return nil, fmt.Errorf("remove container: %w", err) + return nil, fmt.Errorf("docker remove: %w", err) } if err := checkError( @@ -221,19 +221,19 @@ func (s *ContainerService) Remove( }} } - return NewResponse(containerActionCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerActionCollectionFromGen(resp.JSON202), resp.Body), nil } // Exec executes a command inside a running container on the target host. -func (s *ContainerService) Exec( +func (s *DockerService) Exec( ctx context.Context, hostname string, id string, - body gen.ContainerExecRequest, -) (*Response[Collection[ContainerExecResult]], error) { - resp, err := s.client.PostNodeContainerExecWithResponse(ctx, hostname, id, body) + body gen.DockerExecRequest, +) (*Response[Collection[DockerExecResult]], error) { + resp, err := s.client.PostNodeContainerDockerExecWithResponse(ctx, hostname, id, body) if err != nil { - return nil, fmt.Errorf("exec in container: %w", err) + return nil, fmt.Errorf("docker exec: %w", err) } if err := checkError( @@ -254,18 +254,18 @@ func (s *ContainerService) Exec( }} } - return NewResponse(containerExecCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerExecCollectionFromGen(resp.JSON202), resp.Body), nil } // Pull pulls a container image on the target host. -func (s *ContainerService) Pull( +func (s *DockerService) Pull( ctx context.Context, hostname string, - body gen.ContainerPullRequest, -) (*Response[Collection[ContainerPullResult]], error) { - resp, err := s.client.PostNodeContainerPullWithResponse(ctx, hostname, body) + body gen.DockerPullRequest, +) (*Response[Collection[DockerPullResult]], error) { + resp, err := s.client.PostNodeContainerDockerPullWithResponse(ctx, hostname, body) if err != nil { - return nil, fmt.Errorf("pull image: %w", err) + return nil, fmt.Errorf("docker pull: %w", err) } if err := checkError( @@ -285,5 +285,5 @@ func (s *ContainerService) Pull( }} } - return NewResponse(containerPullCollectionFromGen(resp.JSON202), resp.Body), nil + return NewResponse(dockerPullCollectionFromGen(resp.JSON202), resp.Body), nil } diff --git a/pkg/sdk/client/container_public_test.go b/pkg/sdk/client/docker_public_test.go similarity index 83% rename from pkg/sdk/client/container_public_test.go rename to pkg/sdk/client/docker_public_test.go index 70fdf78e..57bb154b 100644 --- a/pkg/sdk/client/container_public_test.go +++ b/pkg/sdk/client/docker_public_test.go @@ -34,22 +34,22 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -type ContainerPublicTestSuite struct { +type DockerPublicTestSuite struct { suite.Suite ctx context.Context } -func (suite *ContainerPublicTestSuite) SetupTest() { +func (suite *DockerPublicTestSuite) SetupTest() { suite.ctx = context.Background() } -func (suite *ContainerPublicTestSuite) TestCreate() { +func (suite *DockerPublicTestSuite) TestCreate() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerResult]], error) }{ { name: "when creating container returns result", @@ -63,7 +63,7 @@ func (suite *ContainerPublicTestSuite) TestCreate() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerResult]], + resp *client.Response[client.Collection[client.DockerResult]], err error, ) { suite.NoError(err) @@ -87,7 +87,7 @@ func (suite *ContainerPublicTestSuite) TestCreate() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerResult]], + resp *client.Response[client.Collection[client.DockerResult]], err error, ) { suite.Error(err) @@ -102,12 +102,12 @@ func (suite *ContainerPublicTestSuite) TestCreate() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerResult]], + resp *client.Response[client.Collection[client.DockerResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "create container") + suite.Contains(err.Error(), "docker create") }, }, { @@ -116,7 +116,7 @@ func (suite *ContainerPublicTestSuite) TestCreate() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerResult]], + resp *client.Response[client.Collection[client.DockerResult]], err error, ) { suite.Error(err) @@ -153,10 +153,10 @@ func (suite *ContainerPublicTestSuite) TestCreate() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Create( + resp, err := sut.Docker.Create( suite.ctx, "_any", - gen.ContainerCreateRequest{ + gen.DockerCreateRequest{ Image: "nginx:latest", Name: strPtr("my-nginx"), }, @@ -166,12 +166,12 @@ func (suite *ContainerPublicTestSuite) TestCreate() { } } -func (suite *ContainerPublicTestSuite) TestList() { +func (suite *DockerPublicTestSuite) TestList() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerListResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerListResult]], error) }{ { name: "when listing containers returns results", @@ -185,7 +185,7 @@ func (suite *ContainerPublicTestSuite) TestList() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerListResult]], + resp *client.Response[client.Collection[client.DockerListResult]], err error, ) { suite.NoError(err) @@ -206,7 +206,7 @@ func (suite *ContainerPublicTestSuite) TestList() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerListResult]], + resp *client.Response[client.Collection[client.DockerListResult]], err error, ) { suite.Error(err) @@ -221,12 +221,12 @@ func (suite *ContainerPublicTestSuite) TestList() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerListResult]], + resp *client.Response[client.Collection[client.DockerListResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "list containers") + suite.Contains(err.Error(), "docker list") }, }, { @@ -235,7 +235,7 @@ func (suite *ContainerPublicTestSuite) TestList() { w.WriteHeader(http.StatusOK) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerListResult]], + resp *client.Response[client.Collection[client.DockerListResult]], err error, ) { suite.Error(err) @@ -272,18 +272,18 @@ func (suite *ContainerPublicTestSuite) TestList() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.List(suite.ctx, "_any", nil) + resp, err := sut.Docker.List(suite.ctx, "_any", nil) tc.validateFunc(resp, err) }) } } -func (suite *ContainerPublicTestSuite) TestInspect() { +func (suite *DockerPublicTestSuite) TestInspect() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerDetailResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerDetailResult]], error) }{ { name: "when inspecting container returns result", @@ -297,7 +297,7 @@ func (suite *ContainerPublicTestSuite) TestInspect() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerDetailResult]], + resp *client.Response[client.Collection[client.DockerDetailResult]], err error, ) { suite.NoError(err) @@ -327,7 +327,7 @@ func (suite *ContainerPublicTestSuite) TestInspect() { _, _ = w.Write([]byte(`{"error":"container not found"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerDetailResult]], + resp *client.Response[client.Collection[client.DockerDetailResult]], err error, ) { suite.Error(err) @@ -346,7 +346,7 @@ func (suite *ContainerPublicTestSuite) TestInspect() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerDetailResult]], + resp *client.Response[client.Collection[client.DockerDetailResult]], err error, ) { suite.Error(err) @@ -361,12 +361,12 @@ func (suite *ContainerPublicTestSuite) TestInspect() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerDetailResult]], + resp *client.Response[client.Collection[client.DockerDetailResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "inspect container") + suite.Contains(err.Error(), "docker inspect") }, }, { @@ -375,7 +375,7 @@ func (suite *ContainerPublicTestSuite) TestInspect() { w.WriteHeader(http.StatusOK) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerDetailResult]], + resp *client.Response[client.Collection[client.DockerDetailResult]], err error, ) { suite.Error(err) @@ -412,18 +412,18 @@ func (suite *ContainerPublicTestSuite) TestInspect() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Inspect(suite.ctx, "_any", "abc123") + resp, err := sut.Docker.Inspect(suite.ctx, "_any", "abc123") tc.validateFunc(resp, err) }) } } -func (suite *ContainerPublicTestSuite) TestStart() { +func (suite *DockerPublicTestSuite) TestStart() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerActionResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerActionResult]], error) }{ { name: "when starting container returns result", @@ -437,7 +437,7 @@ func (suite *ContainerPublicTestSuite) TestStart() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.NoError(err) @@ -458,7 +458,7 @@ func (suite *ContainerPublicTestSuite) TestStart() { _, _ = w.Write([]byte(`{"error":"container not found"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -477,7 +477,7 @@ func (suite *ContainerPublicTestSuite) TestStart() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -492,12 +492,12 @@ func (suite *ContainerPublicTestSuite) TestStart() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "start container") + suite.Contains(err.Error(), "docker start") }, }, { @@ -506,7 +506,7 @@ func (suite *ContainerPublicTestSuite) TestStart() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -543,18 +543,18 @@ func (suite *ContainerPublicTestSuite) TestStart() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Start(suite.ctx, "_any", "abc123") + resp, err := sut.Docker.Start(suite.ctx, "_any", "abc123") tc.validateFunc(resp, err) }) } } -func (suite *ContainerPublicTestSuite) TestStop() { +func (suite *DockerPublicTestSuite) TestStop() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerActionResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerActionResult]], error) }{ { name: "when stopping container returns result", @@ -568,7 +568,7 @@ func (suite *ContainerPublicTestSuite) TestStop() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.NoError(err) @@ -589,7 +589,7 @@ func (suite *ContainerPublicTestSuite) TestStop() { _, _ = w.Write([]byte(`{"error":"container not found"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -608,7 +608,7 @@ func (suite *ContainerPublicTestSuite) TestStop() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -623,12 +623,12 @@ func (suite *ContainerPublicTestSuite) TestStop() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "stop container") + suite.Contains(err.Error(), "docker stop") }, }, { @@ -637,7 +637,7 @@ func (suite *ContainerPublicTestSuite) TestStop() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -674,23 +674,23 @@ func (suite *ContainerPublicTestSuite) TestStop() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Stop( + resp, err := sut.Docker.Stop( suite.ctx, "_any", "abc123", - gen.ContainerStopRequest{}, + gen.DockerStopRequest{}, ) tc.validateFunc(resp, err) }) } } -func (suite *ContainerPublicTestSuite) TestRemove() { +func (suite *DockerPublicTestSuite) TestRemove() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerActionResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerActionResult]], error) }{ { name: "when removing container returns result", @@ -704,7 +704,7 @@ func (suite *ContainerPublicTestSuite) TestRemove() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.NoError(err) @@ -725,7 +725,7 @@ func (suite *ContainerPublicTestSuite) TestRemove() { _, _ = w.Write([]byte(`{"error":"container not found"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -744,7 +744,7 @@ func (suite *ContainerPublicTestSuite) TestRemove() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -759,12 +759,12 @@ func (suite *ContainerPublicTestSuite) TestRemove() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "remove container") + suite.Contains(err.Error(), "docker remove") }, }, { @@ -773,7 +773,7 @@ func (suite *ContainerPublicTestSuite) TestRemove() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerActionResult]], + resp *client.Response[client.Collection[client.DockerActionResult]], err error, ) { suite.Error(err) @@ -810,18 +810,18 @@ func (suite *ContainerPublicTestSuite) TestRemove() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Remove(suite.ctx, "_any", "abc123", nil) + resp, err := sut.Docker.Remove(suite.ctx, "_any", "abc123", nil) tc.validateFunc(resp, err) }) } } -func (suite *ContainerPublicTestSuite) TestExec() { +func (suite *DockerPublicTestSuite) TestExec() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerExecResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerExecResult]], error) }{ { name: "when executing command returns result", @@ -835,7 +835,7 @@ func (suite *ContainerPublicTestSuite) TestExec() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerExecResult]], + resp *client.Response[client.Collection[client.DockerExecResult]], err error, ) { suite.NoError(err) @@ -857,7 +857,7 @@ func (suite *ContainerPublicTestSuite) TestExec() { _, _ = w.Write([]byte(`{"error":"container not found"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerExecResult]], + resp *client.Response[client.Collection[client.DockerExecResult]], err error, ) { suite.Error(err) @@ -876,7 +876,7 @@ func (suite *ContainerPublicTestSuite) TestExec() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerExecResult]], + resp *client.Response[client.Collection[client.DockerExecResult]], err error, ) { suite.Error(err) @@ -891,12 +891,12 @@ func (suite *ContainerPublicTestSuite) TestExec() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerExecResult]], + resp *client.Response[client.Collection[client.DockerExecResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "exec in container") + suite.Contains(err.Error(), "docker exec") }, }, { @@ -905,7 +905,7 @@ func (suite *ContainerPublicTestSuite) TestExec() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerExecResult]], + resp *client.Response[client.Collection[client.DockerExecResult]], err error, ) { suite.Error(err) @@ -942,11 +942,11 @@ func (suite *ContainerPublicTestSuite) TestExec() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Exec( + resp, err := sut.Docker.Exec( suite.ctx, "_any", "abc123", - gen.ContainerExecRequest{ + gen.DockerExecRequest{ Command: []string{"echo", "hello"}, }, ) @@ -955,12 +955,12 @@ func (suite *ContainerPublicTestSuite) TestExec() { } } -func (suite *ContainerPublicTestSuite) TestPull() { +func (suite *DockerPublicTestSuite) TestPull() { tests := []struct { name string handler http.HandlerFunc serverURL string - validateFunc func(*client.Response[client.Collection[client.ContainerPullResult]], error) + validateFunc func(*client.Response[client.Collection[client.DockerPullResult]], error) }{ { name: "when pulling image returns result", @@ -974,7 +974,7 @@ func (suite *ContainerPublicTestSuite) TestPull() { ) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerPullResult]], + resp *client.Response[client.Collection[client.DockerPullResult]], err error, ) { suite.NoError(err) @@ -996,7 +996,7 @@ func (suite *ContainerPublicTestSuite) TestPull() { _, _ = w.Write([]byte(`{"error":"forbidden"}`)) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerPullResult]], + resp *client.Response[client.Collection[client.DockerPullResult]], err error, ) { suite.Error(err) @@ -1011,12 +1011,12 @@ func (suite *ContainerPublicTestSuite) TestPull() { name: "when client HTTP call fails returns error", serverURL: "http://127.0.0.1:0", validateFunc: func( - resp *client.Response[client.Collection[client.ContainerPullResult]], + resp *client.Response[client.Collection[client.DockerPullResult]], err error, ) { suite.Error(err) suite.Nil(resp) - suite.Contains(err.Error(), "pull image") + suite.Contains(err.Error(), "docker pull") }, }, { @@ -1025,7 +1025,7 @@ func (suite *ContainerPublicTestSuite) TestPull() { w.WriteHeader(http.StatusAccepted) }, validateFunc: func( - resp *client.Response[client.Collection[client.ContainerPullResult]], + resp *client.Response[client.Collection[client.DockerPullResult]], err error, ) { suite.Error(err) @@ -1062,10 +1062,10 @@ func (suite *ContainerPublicTestSuite) TestPull() { client.WithLogger(slog.Default()), ) - resp, err := sut.Container.Pull( + resp, err := sut.Docker.Pull( suite.ctx, "_any", - gen.ContainerPullRequest{ + gen.DockerPullRequest{ Image: "nginx:latest", }, ) @@ -1080,6 +1080,6 @@ func strPtr( return &s } -func TestContainerPublicTestSuite(t *testing.T) { - suite.Run(t, new(ContainerPublicTestSuite)) +func TestDockerPublicTestSuite(t *testing.T) { + suite.Run(t, new(DockerPublicTestSuite)) } diff --git a/pkg/sdk/client/container_types.go b/pkg/sdk/client/docker_types.go similarity index 58% rename from pkg/sdk/client/container_types.go rename to pkg/sdk/client/docker_types.go index 937def43..248531fb 100644 --- a/pkg/sdk/client/container_types.go +++ b/pkg/sdk/client/docker_types.go @@ -24,8 +24,8 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -// ContainerResult represents a container create result from a single agent. -type ContainerResult struct { +// DockerResult represents a docker container create result from a single agent. +type DockerResult struct { Hostname string ID string Name string @@ -36,16 +36,16 @@ type ContainerResult struct { Error string } -// ContainerListResult represents a container list result from a single agent. -type ContainerListResult struct { +// DockerListResult represents a docker container list result from a single agent. +type DockerListResult struct { Hostname string - Containers []ContainerSummaryItem + Containers []DockerSummaryItem Changed bool Error string } -// ContainerSummaryItem represents a brief container summary. -type ContainerSummaryItem struct { +// DockerSummaryItem represents a brief docker container summary. +type DockerSummaryItem struct { ID string Name string Image string @@ -53,8 +53,8 @@ type ContainerSummaryItem struct { Created string } -// ContainerDetailResult represents a container inspect result from a single agent. -type ContainerDetailResult struct { +// DockerDetailResult represents a docker container inspect result from a single agent. +type DockerDetailResult struct { Hostname string ID string Name string @@ -70,8 +70,8 @@ type ContainerDetailResult struct { Error string } -// ContainerActionResult represents a container lifecycle action result from a single agent. -type ContainerActionResult struct { +// DockerActionResult represents a docker container lifecycle action result from a single agent. +type DockerActionResult struct { Hostname string ID string Message string @@ -79,8 +79,8 @@ type ContainerActionResult struct { Error string } -// ContainerExecResult represents a container exec result from a single agent. -type ContainerExecResult struct { +// DockerExecResult represents a docker container exec result from a single agent. +type DockerExecResult struct { Hostname string Stdout string Stderr string @@ -89,8 +89,8 @@ type ContainerExecResult struct { Error string } -// ContainerPullResult represents an image pull result from a single agent. -type ContainerPullResult struct { +// DockerPullResult represents a docker image pull result from a single agent. +type DockerPullResult struct { Hostname string ImageID string Tag string @@ -99,14 +99,14 @@ type ContainerPullResult struct { Error string } -// containerResultCollectionFromGen converts a gen.ContainerResultCollectionResponse -// to a Collection[ContainerResult]. -func containerResultCollectionFromGen( - g *gen.ContainerResultCollectionResponse, -) Collection[ContainerResult] { - results := make([]ContainerResult, 0, len(g.Results)) +// dockerResultCollectionFromGen converts a gen.DockerResultCollectionResponse +// to a Collection[DockerResult]. +func dockerResultCollectionFromGen( + g *gen.DockerResultCollectionResponse, +) Collection[DockerResult] { + results := make([]DockerResult, 0, len(g.Results)) for _, r := range g.Results { - results = append(results, ContainerResult{ + results = append(results, DockerResult{ Hostname: r.Hostname, ID: derefString(r.Id), Name: derefString(r.Name), @@ -118,29 +118,29 @@ func containerResultCollectionFromGen( }) } - return Collection[ContainerResult]{ + return Collection[DockerResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } } -// containerListCollectionFromGen converts a gen.ContainerListCollectionResponse -// to a Collection[ContainerListResult]. -func containerListCollectionFromGen( - g *gen.ContainerListCollectionResponse, -) Collection[ContainerListResult] { - results := make([]ContainerListResult, 0, len(g.Results)) +// dockerListCollectionFromGen converts a gen.DockerListCollectionResponse +// to a Collection[DockerListResult]. +func dockerListCollectionFromGen( + g *gen.DockerListCollectionResponse, +) Collection[DockerListResult] { + results := make([]DockerListResult, 0, len(g.Results)) for _, r := range g.Results { - item := ContainerListResult{ + item := DockerListResult{ Hostname: r.Hostname, Changed: derefBool(r.Changed), Error: derefString(r.Error), } if r.Containers != nil { - containers := make([]ContainerSummaryItem, 0, len(*r.Containers)) + containers := make([]DockerSummaryItem, 0, len(*r.Containers)) for _, c := range *r.Containers { - containers = append(containers, ContainerSummaryItem{ + containers = append(containers, DockerSummaryItem{ ID: derefString(c.Id), Name: derefString(c.Name), Image: derefString(c.Image), @@ -155,20 +155,20 @@ func containerListCollectionFromGen( results = append(results, item) } - return Collection[ContainerListResult]{ + return Collection[DockerListResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } } -// containerDetailCollectionFromGen converts a gen.ContainerDetailCollectionResponse -// to a Collection[ContainerDetailResult]. -func containerDetailCollectionFromGen( - g *gen.ContainerDetailCollectionResponse, -) Collection[ContainerDetailResult] { - results := make([]ContainerDetailResult, 0, len(g.Results)) +// dockerDetailCollectionFromGen converts a gen.DockerDetailCollectionResponse +// to a Collection[DockerDetailResult]. +func dockerDetailCollectionFromGen( + g *gen.DockerDetailCollectionResponse, +) Collection[DockerDetailResult] { + results := make([]DockerDetailResult, 0, len(g.Results)) for _, r := range g.Results { - item := ContainerDetailResult{ + item := DockerDetailResult{ Hostname: r.Hostname, ID: derefString(r.Id), Name: derefString(r.Name), @@ -199,20 +199,20 @@ func containerDetailCollectionFromGen( results = append(results, item) } - return Collection[ContainerDetailResult]{ + return Collection[DockerDetailResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } } -// containerActionCollectionFromGen converts a gen.ContainerActionCollectionResponse -// to a Collection[ContainerActionResult]. -func containerActionCollectionFromGen( - g *gen.ContainerActionCollectionResponse, -) Collection[ContainerActionResult] { - results := make([]ContainerActionResult, 0, len(g.Results)) +// dockerActionCollectionFromGen converts a gen.DockerActionCollectionResponse +// to a Collection[DockerActionResult]. +func dockerActionCollectionFromGen( + g *gen.DockerActionCollectionResponse, +) Collection[DockerActionResult] { + results := make([]DockerActionResult, 0, len(g.Results)) for _, r := range g.Results { - results = append(results, ContainerActionResult{ + results = append(results, DockerActionResult{ Hostname: r.Hostname, ID: derefString(r.Id), Message: derefString(r.Message), @@ -221,20 +221,20 @@ func containerActionCollectionFromGen( }) } - return Collection[ContainerActionResult]{ + return Collection[DockerActionResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } } -// containerExecCollectionFromGen converts a gen.ContainerExecCollectionResponse -// to a Collection[ContainerExecResult]. -func containerExecCollectionFromGen( - g *gen.ContainerExecCollectionResponse, -) Collection[ContainerExecResult] { - results := make([]ContainerExecResult, 0, len(g.Results)) +// dockerExecCollectionFromGen converts a gen.DockerExecCollectionResponse +// to a Collection[DockerExecResult]. +func dockerExecCollectionFromGen( + g *gen.DockerExecCollectionResponse, +) Collection[DockerExecResult] { + results := make([]DockerExecResult, 0, len(g.Results)) for _, r := range g.Results { - results = append(results, ContainerExecResult{ + results = append(results, DockerExecResult{ Hostname: r.Hostname, Stdout: derefString(r.Stdout), Stderr: derefString(r.Stderr), @@ -244,20 +244,20 @@ func containerExecCollectionFromGen( }) } - return Collection[ContainerExecResult]{ + return Collection[DockerExecResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } } -// containerPullCollectionFromGen converts a gen.ContainerPullCollectionResponse -// to a Collection[ContainerPullResult]. -func containerPullCollectionFromGen( - g *gen.ContainerPullCollectionResponse, -) Collection[ContainerPullResult] { - results := make([]ContainerPullResult, 0, len(g.Results)) +// dockerPullCollectionFromGen converts a gen.DockerPullCollectionResponse +// to a Collection[DockerPullResult]. +func dockerPullCollectionFromGen( + g *gen.DockerPullCollectionResponse, +) Collection[DockerPullResult] { + results := make([]DockerPullResult, 0, len(g.Results)) for _, r := range g.Results { - results = append(results, ContainerPullResult{ + results = append(results, DockerPullResult{ Hostname: r.Hostname, ImageID: derefString(r.ImageId), Tag: derefString(r.Tag), @@ -267,7 +267,7 @@ func containerPullCollectionFromGen( }) } - return Collection[ContainerPullResult]{ + return Collection[DockerPullResult]{ Results: results, JobID: jobIDFromGen(g.JobId), } diff --git a/pkg/sdk/client/container_types_test.go b/pkg/sdk/client/docker_types_test.go similarity index 73% rename from pkg/sdk/client/container_types_test.go rename to pkg/sdk/client/docker_types_test.go index 164e6fc4..c7ac560c 100644 --- a/pkg/sdk/client/container_types_test.go +++ b/pkg/sdk/client/docker_types_test.go @@ -29,11 +29,11 @@ import ( "github.com/retr0h/osapi/pkg/sdk/client/gen" ) -type ContainerTypesTestSuite struct { +type DockerTypesTestSuite struct { suite.Suite } -func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerResultCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -43,12 +43,12 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { tests := []struct { name string - input *gen.ContainerResultCollectionResponse - validateFunc func(Collection[ContainerResult]) + input *gen.DockerResultCollectionResponse + validateFunc func(Collection[DockerResult]) }{ { name: "when all fields are populated", - input: func() *gen.ContainerResultCollectionResponse { + input: func() *gen.DockerResultCollectionResponse { id := "abc123" name := "my-nginx" image := "nginx:latest" @@ -56,9 +56,9 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { created := "2026-01-01T00:00:00Z" changed := true - return &gen.ContainerResultCollectionResponse{ + return &gen.DockerResultCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerResponse{ + Results: []gen.DockerResponse{ { Hostname: "web-01", Id: &id, @@ -71,7 +71,7 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerResult]) { + validateFunc: func(c Collection[DockerResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -88,11 +88,11 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { }, { name: "when minimal with error", - input: func() *gen.ContainerResultCollectionResponse { + input: func() *gen.DockerResultCollectionResponse { errMsg := "image not found" - return &gen.ContainerResultCollectionResponse{ - Results: []gen.ContainerResponse{ + return &gen.DockerResultCollectionResponse{ + Results: []gen.DockerResponse{ { Hostname: "web-01", Error: &errMsg, @@ -100,7 +100,7 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerResult]) { + validateFunc: func(c Collection[DockerResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) @@ -119,13 +119,13 @@ func (suite *ContainerTypesTestSuite) TestContainerResultCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerResultCollectionFromGen(tc.input) + result := dockerResultCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerListCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -135,19 +135,19 @@ func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { tests := []struct { name string - input *gen.ContainerListCollectionResponse - validateFunc func(Collection[ContainerListResult]) + input *gen.DockerListCollectionResponse + validateFunc func(Collection[DockerListResult]) }{ { name: "when containers are populated", - input: func() *gen.ContainerListCollectionResponse { + input: func() *gen.DockerListCollectionResponse { changed := false id := "abc123" name := "my-nginx" image := "nginx:latest" state := "running" created := "2026-01-01T00:00:00Z" - containers := []gen.ContainerSummary{ + containers := []gen.DockerSummary{ { Id: &id, Name: &name, @@ -157,9 +157,9 @@ func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { }, } - return &gen.ContainerListCollectionResponse{ + return &gen.DockerListCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerListItem{ + Results: []gen.DockerListItem{ { Hostname: "web-01", Changed: &changed, @@ -168,7 +168,7 @@ func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerListResult]) { + validateFunc: func(c Collection[DockerListResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -186,12 +186,12 @@ func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { }, { name: "when containers is nil", - input: &gen.ContainerListCollectionResponse{ - Results: []gen.ContainerListItem{ + input: &gen.DockerListCollectionResponse{ + Results: []gen.DockerListItem{ {Hostname: "web-01"}, }, }, - validateFunc: func(c Collection[ContainerListResult]) { + validateFunc: func(c Collection[DockerListResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) suite.Equal("web-01", c.Results[0].Hostname) @@ -203,13 +203,13 @@ func (suite *ContainerTypesTestSuite) TestContainerListCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerListCollectionFromGen(tc.input) + result := dockerListCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerDetailCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -219,12 +219,12 @@ func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { tests := []struct { name string - input *gen.ContainerDetailCollectionResponse - validateFunc func(Collection[ContainerDetailResult]) + input *gen.DockerDetailCollectionResponse + validateFunc func(Collection[DockerDetailResult]) }{ { name: "when all fields are populated", - input: func() *gen.ContainerDetailCollectionResponse { + input: func() *gen.DockerDetailCollectionResponse { id := "abc123" name := "my-nginx" image := "nginx:latest" @@ -237,9 +237,9 @@ func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { env := []string{"FOO=bar", "BAZ=qux"} networkSettings := map[string]string{"ip": "172.17.0.2"} - return &gen.ContainerDetailCollectionResponse{ + return &gen.DockerDetailCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerDetailResponse{ + Results: []gen.DockerDetailResponse{ { Hostname: "web-01", Id: &id, @@ -257,7 +257,7 @@ func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerDetailResult]) { + validateFunc: func(c Collection[DockerDetailResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -279,12 +279,12 @@ func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { }, { name: "when optional fields are nil", - input: &gen.ContainerDetailCollectionResponse{ - Results: []gen.ContainerDetailResponse{ + input: &gen.DockerDetailCollectionResponse{ + Results: []gen.DockerDetailResponse{ {Hostname: "web-01"}, }, }, - validateFunc: func(c Collection[ContainerDetailResult]) { + validateFunc: func(c Collection[DockerDetailResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) @@ -308,13 +308,13 @@ func (suite *ContainerTypesTestSuite) TestContainerDetailCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerDetailCollectionFromGen(tc.input) + result := dockerDetailCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerActionCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -324,19 +324,19 @@ func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { tests := []struct { name string - input *gen.ContainerActionCollectionResponse - validateFunc func(Collection[ContainerActionResult]) + input *gen.DockerActionCollectionResponse + validateFunc func(Collection[DockerActionResult]) }{ { name: "when all fields are populated", - input: func() *gen.ContainerActionCollectionResponse { + input: func() *gen.DockerActionCollectionResponse { id := "abc123" message := "container started" changed := true - return &gen.ContainerActionCollectionResponse{ + return &gen.DockerActionCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerActionResultItem{ + Results: []gen.DockerActionResultItem{ { Hostname: "web-01", Id: &id, @@ -346,7 +346,7 @@ func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerActionResult]) { + validateFunc: func(c Collection[DockerActionResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -360,11 +360,11 @@ func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { }, { name: "when minimal with error", - input: func() *gen.ContainerActionCollectionResponse { + input: func() *gen.DockerActionCollectionResponse { errMsg := "container not found" - return &gen.ContainerActionCollectionResponse{ - Results: []gen.ContainerActionResultItem{ + return &gen.DockerActionCollectionResponse{ + Results: []gen.DockerActionResultItem{ { Hostname: "web-01", Error: &errMsg, @@ -372,7 +372,7 @@ func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerActionResult]) { + validateFunc: func(c Collection[DockerActionResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) @@ -388,13 +388,13 @@ func (suite *ContainerTypesTestSuite) TestContainerActionCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerActionCollectionFromGen(tc.input) + result := dockerActionCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerExecCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -404,20 +404,20 @@ func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { tests := []struct { name string - input *gen.ContainerExecCollectionResponse - validateFunc func(Collection[ContainerExecResult]) + input *gen.DockerExecCollectionResponse + validateFunc func(Collection[DockerExecResult]) }{ { name: "when all fields are populated", - input: func() *gen.ContainerExecCollectionResponse { + input: func() *gen.DockerExecCollectionResponse { stdout := "hello world\n" stderr := "warning: something\n" exitCode := 0 changed := true - return &gen.ContainerExecCollectionResponse{ + return &gen.DockerExecCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerExecResultItem{ + Results: []gen.DockerExecResultItem{ { Hostname: "web-01", Stdout: &stdout, @@ -428,7 +428,7 @@ func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerExecResult]) { + validateFunc: func(c Collection[DockerExecResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -443,12 +443,12 @@ func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { }, { name: "when minimal with error", - input: func() *gen.ContainerExecCollectionResponse { + input: func() *gen.DockerExecCollectionResponse { errMsg := "exec failed" exitCode := 1 - return &gen.ContainerExecCollectionResponse{ - Results: []gen.ContainerExecResultItem{ + return &gen.DockerExecCollectionResponse{ + Results: []gen.DockerExecResultItem{ { Hostname: "web-01", Error: &errMsg, @@ -457,7 +457,7 @@ func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerExecResult]) { + validateFunc: func(c Collection[DockerExecResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) @@ -474,13 +474,13 @@ func (suite *ContainerTypesTestSuite) TestContainerExecCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerExecCollectionFromGen(tc.input) + result := dockerExecCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { +func (suite *DockerTypesTestSuite) TestDockerPullCollectionFromGen() { testUUID := openapi_types.UUID{ 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, @@ -490,20 +490,20 @@ func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { tests := []struct { name string - input *gen.ContainerPullCollectionResponse - validateFunc func(Collection[ContainerPullResult]) + input *gen.DockerPullCollectionResponse + validateFunc func(Collection[DockerPullResult]) }{ { name: "when all fields are populated", - input: func() *gen.ContainerPullCollectionResponse { + input: func() *gen.DockerPullCollectionResponse { imageID := "sha256:abc123" tag := "latest" size := int64(52428800) changed := true - return &gen.ContainerPullCollectionResponse{ + return &gen.DockerPullCollectionResponse{ JobId: &testUUID, - Results: []gen.ContainerPullResultItem{ + Results: []gen.DockerPullResultItem{ { Hostname: "web-01", ImageId: &imageID, @@ -514,7 +514,7 @@ func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerPullResult]) { + validateFunc: func(c Collection[DockerPullResult]) { suite.Equal("550e8400-e29b-41d4-a716-446655440000", c.JobID) suite.Require().Len(c.Results, 1) @@ -529,11 +529,11 @@ func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { }, { name: "when minimal with error", - input: func() *gen.ContainerPullCollectionResponse { + input: func() *gen.DockerPullCollectionResponse { errMsg := "pull failed: image not found" - return &gen.ContainerPullCollectionResponse{ - Results: []gen.ContainerPullResultItem{ + return &gen.DockerPullCollectionResponse{ + Results: []gen.DockerPullResultItem{ { Hostname: "web-01", Error: &errMsg, @@ -541,7 +541,7 @@ func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { }, } }(), - validateFunc: func(c Collection[ContainerPullResult]) { + validateFunc: func(c Collection[DockerPullResult]) { suite.Empty(c.JobID) suite.Require().Len(c.Results, 1) @@ -558,12 +558,12 @@ func (suite *ContainerTypesTestSuite) TestContainerPullCollectionFromGen() { for _, tc := range tests { suite.Run(tc.name, func() { - result := containerPullCollectionFromGen(tc.input) + result := dockerPullCollectionFromGen(tc.input) tc.validateFunc(result) }) } } -func TestContainerTypesTestSuite(t *testing.T) { - suite.Run(t, new(ContainerTypesTestSuite)) +func TestDockerTypesTestSuite(t *testing.T) { + suite.Run(t, new(DockerTypesTestSuite)) } diff --git a/pkg/sdk/client/gen/client.gen.go b/pkg/sdk/client/gen/client.gen.go index 4e597011..f7cb1f41 100644 --- a/pkg/sdk/client/gen/client.gen.go +++ b/pkg/sdk/client/gen/client.gen.go @@ -76,11 +76,11 @@ const ( GetJobParamsStatusSubmitted GetJobParamsStatus = "submitted" ) -// Defines values for GetNodeContainerParamsState. +// Defines values for GetNodeContainerDockerParamsState. const ( - All GetNodeContainerParamsState = "all" - Running GetNodeContainerParamsState = "running" - Stopped GetNodeContainerParamsState = "stopped" + All GetNodeContainerDockerParamsState = "all" + Running GetNodeContainerDockerParamsState = "running" + Stopped GetNodeContainerDockerParamsState = "stopped" ) // AgentDetail defines model for AgentDetail. @@ -309,15 +309,126 @@ type ConsumerStats struct { Total int `json:"total"` } -// ContainerActionCollectionResponse defines model for ContainerActionCollectionResponse. -type ContainerActionCollectionResponse struct { +// CreateJobResponse defines model for CreateJobResponse. +type CreateJobResponse struct { + // JobId Unique identifier for the created job. + JobId openapi_types.UUID `json:"job_id"` + + // Revision The KV revision number. + Revision *int64 `json:"revision,omitempty"` + + // Status Initial status of the job. + Status string `json:"status"` + + // Timestamp Creation timestamp. + Timestamp *string `json:"timestamp,omitempty"` +} + +// DNSConfigCollectionResponse defines model for DNSConfigCollectionResponse. +type DNSConfigCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DNSConfigResponse `json:"results"` +} + +// DNSConfigResponse defines model for DNSConfigResponse. +type DNSConfigResponse struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent that served this config. + Hostname string `json:"hostname"` + + // SearchDomains List of search domains. + SearchDomains *[]string `json:"search_domains,omitempty"` + + // Servers List of configured DNS servers. + Servers *[]string `json:"servers,omitempty"` +} + +// DNSConfigUpdateRequest defines model for DNSConfigUpdateRequest. +type DNSConfigUpdateRequest struct { + // InterfaceName The name of the network interface to apply DNS configuration to. Accepts alphanumeric names or @fact. references. + InterfaceName string `json:"interface_name" validate:"required,alphanum_or_fact"` + + // SearchDomains New list of search domains to configure. + SearchDomains *[]string `json:"search_domains,omitempty" validate:"required_without=Servers,omitempty,dive,hostname,min=1"` + + // Servers New list of DNS servers to configure. + Servers *[]string `json:"servers,omitempty" validate:"required_without=SearchDomains,omitempty,dive,ip,min=1"` +} + +// DNSUpdateCollectionResponse defines model for DNSUpdateCollectionResponse. +type DNSUpdateCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DNSUpdateResultItem `json:"results"` +} + +// DNSUpdateResultItem defines model for DNSUpdateResultItem. +type DNSUpdateResultItem struct { + // Changed Whether the DNS configuration was actually modified. + Changed *bool `json:"changed,omitempty"` + Error *string `json:"error,omitempty"` + Hostname string `json:"hostname"` + Status DNSUpdateResultItemStatus `json:"status"` +} + +// DNSUpdateResultItemStatus defines model for DNSUpdateResultItem.Status. +type DNSUpdateResultItemStatus string + +// DiskCollectionResponse defines model for DiskCollectionResponse. +type DiskCollectionResponse struct { + // JobId The job ID used to process this request. + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DiskResultItem `json:"results"` +} + +// DiskResponse Local disk usage information. +type DiskResponse struct { + // Free Free disk space in bytes. + Free int `json:"free"` + + // Name Disk identifier, e.g., "/dev/sda1". + Name string `json:"name"` + + // Total Total disk space in bytes. + Total int `json:"total"` + + // Used Used disk space in bytes. + Used int `json:"used"` +} + +// DiskResultItem defines model for DiskResultItem. +type DiskResultItem struct { + // Changed Whether the operation modified system state. + Changed *bool `json:"changed,omitempty"` + + // Disks List of local disk usage information. + Disks *DisksResponse `json:"disks,omitempty"` + + // Error Error message if the agent failed. + Error *string `json:"error,omitempty"` + + // Hostname The hostname of the agent. + Hostname string `json:"hostname"` +} + +// DisksResponse List of local disk usage information. +type DisksResponse = []DiskResponse + +// DockerActionCollectionResponse defines model for DockerActionCollectionResponse. +type DockerActionCollectionResponse struct { // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerActionResultItem `json:"results"` + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerActionResultItem `json:"results"` } -// ContainerActionResultItem Result of a container lifecycle action. -type ContainerActionResultItem struct { +// DockerActionResultItem Result of a container lifecycle action. +type DockerActionResultItem struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` @@ -334,8 +445,8 @@ type ContainerActionResultItem struct { Message *string `json:"message,omitempty"` } -// ContainerCreateRequest defines model for ContainerCreateRequest. -type ContainerCreateRequest struct { +// DockerCreateRequest defines model for DockerCreateRequest. +type DockerCreateRequest struct { // AutoStart Whether to start the container immediately after creation. Defaults to true. AutoStart *bool `json:"auto_start,omitempty"` @@ -358,15 +469,15 @@ type ContainerCreateRequest struct { Volumes *[]string `json:"volumes,omitempty"` } -// ContainerDetailCollectionResponse defines model for ContainerDetailCollectionResponse. -type ContainerDetailCollectionResponse struct { +// DockerDetailCollectionResponse defines model for DockerDetailCollectionResponse. +type DockerDetailCollectionResponse struct { // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerDetailResponse `json:"results"` + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerDetailResponse `json:"results"` } -// ContainerDetailResponse Detailed information about a container. -type ContainerDetailResponse struct { +// DockerDetailResponse Detailed information about a container. +type DockerDetailResponse struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` @@ -407,15 +518,15 @@ type ContainerDetailResponse struct { State *string `json:"state,omitempty"` } -// ContainerExecCollectionResponse defines model for ContainerExecCollectionResponse. -type ContainerExecCollectionResponse struct { +// DockerExecCollectionResponse defines model for DockerExecCollectionResponse. +type DockerExecCollectionResponse struct { // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerExecResultItem `json:"results"` + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerExecResultItem `json:"results"` } -// ContainerExecRequest defines model for ContainerExecRequest. -type ContainerExecRequest struct { +// DockerExecRequest defines model for DockerExecRequest. +type DockerExecRequest struct { // Command Command to execute inside the container. Command []string `json:"command" validate:"required,min=1"` @@ -426,8 +537,8 @@ type ContainerExecRequest struct { WorkingDir *string `json:"working_dir,omitempty"` } -// ContainerExecResultItem Result of a command execution inside a container. -type ContainerExecResultItem struct { +// DockerExecResultItem Result of a command execution inside a container. +type DockerExecResultItem struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` @@ -447,20 +558,20 @@ type ContainerExecResultItem struct { Stdout *string `json:"stdout,omitempty"` } -// ContainerListCollectionResponse defines model for ContainerListCollectionResponse. -type ContainerListCollectionResponse struct { +// DockerListCollectionResponse defines model for DockerListCollectionResponse. +type DockerListCollectionResponse struct { // JobId The job ID used to process this request. JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerListItem `json:"results"` + Results []DockerListItem `json:"results"` } -// ContainerListItem Container summary for list operations. -type ContainerListItem struct { +// DockerListItem Container summary for list operations. +type DockerListItem struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` // Containers List of containers on this agent. - Containers *[]ContainerSummary `json:"containers,omitempty"` + Containers *[]DockerSummary `json:"containers,omitempty"` // Error Error message if the agent failed. Error *string `json:"error,omitempty"` @@ -469,21 +580,21 @@ type ContainerListItem struct { Hostname string `json:"hostname"` } -// ContainerPullCollectionResponse defines model for ContainerPullCollectionResponse. -type ContainerPullCollectionResponse struct { +// DockerPullCollectionResponse defines model for DockerPullCollectionResponse. +type DockerPullCollectionResponse struct { // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerPullResultItem `json:"results"` + JobId *openapi_types.UUID `json:"job_id,omitempty"` + Results []DockerPullResultItem `json:"results"` } -// ContainerPullRequest defines model for ContainerPullRequest. -type ContainerPullRequest struct { +// DockerPullRequest defines model for DockerPullRequest. +type DockerPullRequest struct { // Image Image reference to pull (e.g., "nginx:latest", "docker.io/library/alpine:3.18"). Image string `json:"image" validate:"required,min=1"` } -// ContainerPullResultItem Result of an image pull operation. -type ContainerPullResultItem struct { +// DockerPullResultItem Result of an image pull operation. +type DockerPullResultItem struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` @@ -503,8 +614,8 @@ type ContainerPullResultItem struct { Tag *string `json:"tag,omitempty"` } -// ContainerResponse Summary information about a container. -type ContainerResponse struct { +// DockerResponse Summary information about a container. +type DockerResponse struct { // Changed Whether the operation modified system state. Changed *bool `json:"changed,omitempty"` @@ -530,21 +641,21 @@ type ContainerResponse struct { State *string `json:"state,omitempty"` } -// ContainerResultCollectionResponse defines model for ContainerResultCollectionResponse. -type ContainerResultCollectionResponse struct { +// DockerResultCollectionResponse defines model for DockerResultCollectionResponse. +type DockerResultCollectionResponse struct { // JobId The job ID used to process this request. JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []ContainerResponse `json:"results"` + Results []DockerResponse `json:"results"` } -// ContainerStopRequest defines model for ContainerStopRequest. -type ContainerStopRequest struct { +// DockerStopRequest defines model for DockerStopRequest. +type DockerStopRequest struct { // Timeout Seconds to wait before killing the container. Defaults to 10. Timeout *int `json:"timeout,omitempty" validate:"omitempty,min=0,max=300"` } -// ContainerSummary Brief container summary. -type ContainerSummary struct { +// DockerSummary Brief container summary. +type DockerSummary struct { // Created Container creation timestamp. Created *string `json:"created,omitempty"` @@ -561,117 +672,6 @@ type ContainerSummary struct { State *string `json:"state,omitempty"` } -// CreateJobResponse defines model for CreateJobResponse. -type CreateJobResponse struct { - // JobId Unique identifier for the created job. - JobId openapi_types.UUID `json:"job_id"` - - // Revision The KV revision number. - Revision *int64 `json:"revision,omitempty"` - - // Status Initial status of the job. - Status string `json:"status"` - - // Timestamp Creation timestamp. - Timestamp *string `json:"timestamp,omitempty"` -} - -// DNSConfigCollectionResponse defines model for DNSConfigCollectionResponse. -type DNSConfigCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []DNSConfigResponse `json:"results"` -} - -// DNSConfigResponse defines model for DNSConfigResponse. -type DNSConfigResponse struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent that served this config. - Hostname string `json:"hostname"` - - // SearchDomains List of search domains. - SearchDomains *[]string `json:"search_domains,omitempty"` - - // Servers List of configured DNS servers. - Servers *[]string `json:"servers,omitempty"` -} - -// DNSConfigUpdateRequest defines model for DNSConfigUpdateRequest. -type DNSConfigUpdateRequest struct { - // InterfaceName The name of the network interface to apply DNS configuration to. Accepts alphanumeric names or @fact. references. - InterfaceName string `json:"interface_name" validate:"required,alphanum_or_fact"` - - // SearchDomains New list of search domains to configure. - SearchDomains *[]string `json:"search_domains,omitempty" validate:"required_without=Servers,omitempty,dive,hostname,min=1"` - - // Servers New list of DNS servers to configure. - Servers *[]string `json:"servers,omitempty" validate:"required_without=SearchDomains,omitempty,dive,ip,min=1"` -} - -// DNSUpdateCollectionResponse defines model for DNSUpdateCollectionResponse. -type DNSUpdateCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []DNSUpdateResultItem `json:"results"` -} - -// DNSUpdateResultItem defines model for DNSUpdateResultItem. -type DNSUpdateResultItem struct { - // Changed Whether the DNS configuration was actually modified. - Changed *bool `json:"changed,omitempty"` - Error *string `json:"error,omitempty"` - Hostname string `json:"hostname"` - Status DNSUpdateResultItemStatus `json:"status"` -} - -// DNSUpdateResultItemStatus defines model for DNSUpdateResultItem.Status. -type DNSUpdateResultItemStatus string - -// DiskCollectionResponse defines model for DiskCollectionResponse. -type DiskCollectionResponse struct { - // JobId The job ID used to process this request. - JobId *openapi_types.UUID `json:"job_id,omitempty"` - Results []DiskResultItem `json:"results"` -} - -// DiskResponse Local disk usage information. -type DiskResponse struct { - // Free Free disk space in bytes. - Free int `json:"free"` - - // Name Disk identifier, e.g., "/dev/sda1". - Name string `json:"name"` - - // Total Total disk space in bytes. - Total int `json:"total"` - - // Used Used disk space in bytes. - Used int `json:"used"` -} - -// DiskResultItem defines model for DiskResultItem. -type DiskResultItem struct { - // Changed Whether the operation modified system state. - Changed *bool `json:"changed,omitempty"` - - // Disks List of local disk usage information. - Disks *DisksResponse `json:"disks,omitempty"` - - // Error Error message if the agent failed. - Error *string `json:"error,omitempty"` - - // Hostname The hostname of the agent. - Hostname string `json:"hostname"` -} - -// DisksResponse List of local disk usage information. -type DisksResponse = []DiskResponse - // ErrorResponse defines model for ErrorResponse. type ErrorResponse struct { // Code The error code. @@ -1297,8 +1297,8 @@ type UptimeResponse struct { Uptime *string `json:"uptime,omitempty"` } -// ContainerId defines model for ContainerId. -type ContainerId = string +// DockerId defines model for DockerId. +type DockerId = string // FileName defines model for FileName. type FileName = string @@ -1351,20 +1351,20 @@ type GetJobParams struct { // GetJobParamsStatus defines parameters for GetJob. type GetJobParamsStatus string -// GetNodeContainerParams defines parameters for GetNodeContainer. -type GetNodeContainerParams struct { +// GetNodeContainerDockerParams defines parameters for GetNodeContainerDocker. +type GetNodeContainerDockerParams struct { // State Filter containers by state. Defaults to "all". - State *GetNodeContainerParamsState `form:"state,omitempty" json:"state,omitempty" validate:"omitempty,oneof=running stopped all"` + State *GetNodeContainerDockerParamsState `form:"state,omitempty" json:"state,omitempty" validate:"omitempty,oneof=running stopped all"` // Limit Maximum number of containers to return. Limit *int `form:"limit,omitempty" json:"limit,omitempty" validate:"omitempty,min=1,max=100"` } -// GetNodeContainerParamsState defines parameters for GetNodeContainer. -type GetNodeContainerParamsState string +// GetNodeContainerDockerParamsState defines parameters for GetNodeContainerDocker. +type GetNodeContainerDockerParamsState string -// DeleteNodeContainerByIDParams defines parameters for DeleteNodeContainerByID. -type DeleteNodeContainerByIDParams struct { +// DeleteNodeContainerDockerByIDParams defines parameters for DeleteNodeContainerDockerByID. +type DeleteNodeContainerDockerByIDParams struct { // Force Force removal of a running container. Force *bool `form:"force,omitempty" json:"force,omitempty" validate:"omitempty"` } @@ -1387,17 +1387,17 @@ type PostNodeCommandExecJSONRequestBody = CommandExecRequest // PostNodeCommandShellJSONRequestBody defines body for PostNodeCommandShell for application/json ContentType. type PostNodeCommandShellJSONRequestBody = CommandShellRequest -// PostNodeContainerJSONRequestBody defines body for PostNodeContainer for application/json ContentType. -type PostNodeContainerJSONRequestBody = ContainerCreateRequest +// PostNodeContainerDockerJSONRequestBody defines body for PostNodeContainerDocker for application/json ContentType. +type PostNodeContainerDockerJSONRequestBody = DockerCreateRequest -// PostNodeContainerPullJSONRequestBody defines body for PostNodeContainerPull for application/json ContentType. -type PostNodeContainerPullJSONRequestBody = ContainerPullRequest +// PostNodeContainerDockerPullJSONRequestBody defines body for PostNodeContainerDockerPull for application/json ContentType. +type PostNodeContainerDockerPullJSONRequestBody = DockerPullRequest -// PostNodeContainerExecJSONRequestBody defines body for PostNodeContainerExec for application/json ContentType. -type PostNodeContainerExecJSONRequestBody = ContainerExecRequest +// PostNodeContainerDockerExecJSONRequestBody defines body for PostNodeContainerDockerExec for application/json ContentType. +type PostNodeContainerDockerExecJSONRequestBody = DockerExecRequest -// PostNodeContainerStopJSONRequestBody defines body for PostNodeContainerStop for application/json ContentType. -type PostNodeContainerStopJSONRequestBody = ContainerStopRequest +// PostNodeContainerDockerStopJSONRequestBody defines body for PostNodeContainerDockerStop for application/json ContentType. +type PostNodeContainerDockerStopJSONRequestBody = DockerStopRequest // PostNodeFileDeployJSONRequestBody defines body for PostNodeFileDeploy for application/json ContentType. type PostNodeFileDeployJSONRequestBody = FileDeployRequest @@ -1553,37 +1553,37 @@ type ClientInterface interface { PostNodeCommandShell(ctx context.Context, hostname Hostname, body PostNodeCommandShellJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetNodeContainer request - GetNodeContainer(ctx context.Context, hostname Hostname, params *GetNodeContainerParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetNodeContainerDocker request + GetNodeContainerDocker(ctx context.Context, hostname Hostname, params *GetNodeContainerDockerParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostNodeContainerWithBody request with any body - PostNodeContainerWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeContainerDockerWithBody request with any body + PostNodeContainerDockerWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostNodeContainer(ctx context.Context, hostname Hostname, body PostNodeContainerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostNodeContainerDocker(ctx context.Context, hostname Hostname, body PostNodeContainerDockerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostNodeContainerPullWithBody request with any body - PostNodeContainerPullWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeContainerDockerPullWithBody request with any body + PostNodeContainerDockerPullWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostNodeContainerPull(ctx context.Context, hostname Hostname, body PostNodeContainerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostNodeContainerDockerPull(ctx context.Context, hostname Hostname, body PostNodeContainerDockerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteNodeContainerByID request - DeleteNodeContainerByID(ctx context.Context, hostname Hostname, id ContainerId, params *DeleteNodeContainerByIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteNodeContainerDockerByID request + DeleteNodeContainerDockerByID(ctx context.Context, hostname Hostname, id DockerId, params *DeleteNodeContainerDockerByIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetNodeContainerByID request - GetNodeContainerByID(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetNodeContainerDockerByID request + GetNodeContainerDockerByID(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostNodeContainerExecWithBody request with any body - PostNodeContainerExecWithBody(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeContainerDockerExecWithBody request with any body + PostNodeContainerDockerExecWithBody(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostNodeContainerExec(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostNodeContainerDockerExec(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostNodeContainerStart request - PostNodeContainerStart(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeContainerDockerStart request + PostNodeContainerDockerStart(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostNodeContainerStopWithBody request with any body - PostNodeContainerStopWithBody(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeContainerDockerStopWithBody request with any body + PostNodeContainerDockerStopWithBody(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostNodeContainerStop(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostNodeContainerDockerStop(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetNodeDisk request GetNodeDisk(ctx context.Context, hostname Hostname, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1918,8 +1918,8 @@ func (c *Client) PostNodeCommandShell(ctx context.Context, hostname Hostname, bo return c.Client.Do(req) } -func (c *Client) GetNodeContainer(ctx context.Context, hostname Hostname, params *GetNodeContainerParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetNodeContainerRequest(c.Server, hostname, params) +func (c *Client) GetNodeContainerDocker(ctx context.Context, hostname Hostname, params *GetNodeContainerDockerParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetNodeContainerDockerRequest(c.Server, hostname, params) if err != nil { return nil, err } @@ -1930,8 +1930,8 @@ func (c *Client) GetNodeContainer(ctx context.Context, hostname Hostname, params return c.Client.Do(req) } -func (c *Client) PostNodeContainerWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerRequestWithBody(c.Server, hostname, contentType, body) +func (c *Client) PostNodeContainerDockerWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerRequestWithBody(c.Server, hostname, contentType, body) if err != nil { return nil, err } @@ -1942,8 +1942,8 @@ func (c *Client) PostNodeContainerWithBody(ctx context.Context, hostname Hostnam return c.Client.Do(req) } -func (c *Client) PostNodeContainer(ctx context.Context, hostname Hostname, body PostNodeContainerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerRequest(c.Server, hostname, body) +func (c *Client) PostNodeContainerDocker(ctx context.Context, hostname Hostname, body PostNodeContainerDockerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerRequest(c.Server, hostname, body) if err != nil { return nil, err } @@ -1954,8 +1954,8 @@ func (c *Client) PostNodeContainer(ctx context.Context, hostname Hostname, body return c.Client.Do(req) } -func (c *Client) PostNodeContainerPullWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerPullRequestWithBody(c.Server, hostname, contentType, body) +func (c *Client) PostNodeContainerDockerPullWithBody(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerPullRequestWithBody(c.Server, hostname, contentType, body) if err != nil { return nil, err } @@ -1966,8 +1966,8 @@ func (c *Client) PostNodeContainerPullWithBody(ctx context.Context, hostname Hos return c.Client.Do(req) } -func (c *Client) PostNodeContainerPull(ctx context.Context, hostname Hostname, body PostNodeContainerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerPullRequest(c.Server, hostname, body) +func (c *Client) PostNodeContainerDockerPull(ctx context.Context, hostname Hostname, body PostNodeContainerDockerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerPullRequest(c.Server, hostname, body) if err != nil { return nil, err } @@ -1978,8 +1978,8 @@ func (c *Client) PostNodeContainerPull(ctx context.Context, hostname Hostname, b return c.Client.Do(req) } -func (c *Client) DeleteNodeContainerByID(ctx context.Context, hostname Hostname, id ContainerId, params *DeleteNodeContainerByIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteNodeContainerByIDRequest(c.Server, hostname, id, params) +func (c *Client) DeleteNodeContainerDockerByID(ctx context.Context, hostname Hostname, id DockerId, params *DeleteNodeContainerDockerByIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteNodeContainerDockerByIDRequest(c.Server, hostname, id, params) if err != nil { return nil, err } @@ -1990,8 +1990,8 @@ func (c *Client) DeleteNodeContainerByID(ctx context.Context, hostname Hostname, return c.Client.Do(req) } -func (c *Client) GetNodeContainerByID(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetNodeContainerByIDRequest(c.Server, hostname, id) +func (c *Client) GetNodeContainerDockerByID(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetNodeContainerDockerByIDRequest(c.Server, hostname, id) if err != nil { return nil, err } @@ -2002,8 +2002,8 @@ func (c *Client) GetNodeContainerByID(ctx context.Context, hostname Hostname, id return c.Client.Do(req) } -func (c *Client) PostNodeContainerExecWithBody(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerExecRequestWithBody(c.Server, hostname, id, contentType, body) +func (c *Client) PostNodeContainerDockerExecWithBody(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerExecRequestWithBody(c.Server, hostname, id, contentType, body) if err != nil { return nil, err } @@ -2014,8 +2014,8 @@ func (c *Client) PostNodeContainerExecWithBody(ctx context.Context, hostname Hos return c.Client.Do(req) } -func (c *Client) PostNodeContainerExec(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerExecRequest(c.Server, hostname, id, body) +func (c *Client) PostNodeContainerDockerExec(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerExecRequest(c.Server, hostname, id, body) if err != nil { return nil, err } @@ -2026,8 +2026,8 @@ func (c *Client) PostNodeContainerExec(ctx context.Context, hostname Hostname, i return c.Client.Do(req) } -func (c *Client) PostNodeContainerStart(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerStartRequest(c.Server, hostname, id) +func (c *Client) PostNodeContainerDockerStart(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerStartRequest(c.Server, hostname, id) if err != nil { return nil, err } @@ -2038,8 +2038,8 @@ func (c *Client) PostNodeContainerStart(ctx context.Context, hostname Hostname, return c.Client.Do(req) } -func (c *Client) PostNodeContainerStopWithBody(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerStopRequestWithBody(c.Server, hostname, id, contentType, body) +func (c *Client) PostNodeContainerDockerStopWithBody(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerStopRequestWithBody(c.Server, hostname, id, contentType, body) if err != nil { return nil, err } @@ -2050,8 +2050,8 @@ func (c *Client) PostNodeContainerStopWithBody(ctx context.Context, hostname Hos return c.Client.Do(req) } -func (c *Client) PostNodeContainerStop(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostNodeContainerStopRequest(c.Server, hostname, id, body) +func (c *Client) PostNodeContainerDockerStop(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeContainerDockerStopRequest(c.Server, hostname, id, body) if err != nil { return nil, err } @@ -3060,8 +3060,8 @@ func NewPostNodeCommandShellRequestWithBody(server string, hostname Hostname, co return req, nil } -// NewGetNodeContainerRequest generates requests for GetNodeContainer -func NewGetNodeContainerRequest(server string, hostname Hostname, params *GetNodeContainerParams) (*http.Request, error) { +// NewGetNodeContainerDockerRequest generates requests for GetNodeContainerDocker +func NewGetNodeContainerDockerRequest(server string, hostname Hostname, params *GetNodeContainerDockerParams) (*http.Request, error) { var err error var pathParam0 string @@ -3076,7 +3076,7 @@ func NewGetNodeContainerRequest(server string, hostname Hostname, params *GetNod return nil, err } - operationPath := fmt.Sprintf("/node/%s/container", pathParam0) + operationPath := fmt.Sprintf("/node/%s/container/docker", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3132,19 +3132,19 @@ func NewGetNodeContainerRequest(server string, hostname Hostname, params *GetNod return req, nil } -// NewPostNodeContainerRequest calls the generic PostNodeContainer builder with application/json body -func NewPostNodeContainerRequest(server string, hostname Hostname, body PostNodeContainerJSONRequestBody) (*http.Request, error) { +// NewPostNodeContainerDockerRequest calls the generic PostNodeContainerDocker builder with application/json body +func NewPostNodeContainerDockerRequest(server string, hostname Hostname, body PostNodeContainerDockerJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostNodeContainerRequestWithBody(server, hostname, "application/json", bodyReader) + return NewPostNodeContainerDockerRequestWithBody(server, hostname, "application/json", bodyReader) } -// NewPostNodeContainerRequestWithBody generates requests for PostNodeContainer with any type of body -func NewPostNodeContainerRequestWithBody(server string, hostname Hostname, contentType string, body io.Reader) (*http.Request, error) { +// NewPostNodeContainerDockerRequestWithBody generates requests for PostNodeContainerDocker with any type of body +func NewPostNodeContainerDockerRequestWithBody(server string, hostname Hostname, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -3159,7 +3159,7 @@ func NewPostNodeContainerRequestWithBody(server string, hostname Hostname, conte return nil, err } - operationPath := fmt.Sprintf("/node/%s/container", pathParam0) + operationPath := fmt.Sprintf("/node/%s/container/docker", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3179,19 +3179,19 @@ func NewPostNodeContainerRequestWithBody(server string, hostname Hostname, conte return req, nil } -// NewPostNodeContainerPullRequest calls the generic PostNodeContainerPull builder with application/json body -func NewPostNodeContainerPullRequest(server string, hostname Hostname, body PostNodeContainerPullJSONRequestBody) (*http.Request, error) { +// NewPostNodeContainerDockerPullRequest calls the generic PostNodeContainerDockerPull builder with application/json body +func NewPostNodeContainerDockerPullRequest(server string, hostname Hostname, body PostNodeContainerDockerPullJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostNodeContainerPullRequestWithBody(server, hostname, "application/json", bodyReader) + return NewPostNodeContainerDockerPullRequestWithBody(server, hostname, "application/json", bodyReader) } -// NewPostNodeContainerPullRequestWithBody generates requests for PostNodeContainerPull with any type of body -func NewPostNodeContainerPullRequestWithBody(server string, hostname Hostname, contentType string, body io.Reader) (*http.Request, error) { +// NewPostNodeContainerDockerPullRequestWithBody generates requests for PostNodeContainerDockerPull with any type of body +func NewPostNodeContainerDockerPullRequestWithBody(server string, hostname Hostname, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -3206,7 +3206,7 @@ func NewPostNodeContainerPullRequestWithBody(server string, hostname Hostname, c return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/pull", pathParam0) + operationPath := fmt.Sprintf("/node/%s/container/docker/pull", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3226,8 +3226,8 @@ func NewPostNodeContainerPullRequestWithBody(server string, hostname Hostname, c return req, nil } -// NewDeleteNodeContainerByIDRequest generates requests for DeleteNodeContainerByID -func NewDeleteNodeContainerByIDRequest(server string, hostname Hostname, id ContainerId, params *DeleteNodeContainerByIDParams) (*http.Request, error) { +// NewDeleteNodeContainerDockerByIDRequest generates requests for DeleteNodeContainerDockerByID +func NewDeleteNodeContainerDockerByIDRequest(server string, hostname Hostname, id DockerId, params *DeleteNodeContainerDockerByIDParams) (*http.Request, error) { var err error var pathParam0 string @@ -3249,7 +3249,7 @@ func NewDeleteNodeContainerByIDRequest(server string, hostname Hostname, id Cont return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/%s", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/node/%s/container/docker/%s", pathParam0, pathParam1) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3289,8 +3289,8 @@ func NewDeleteNodeContainerByIDRequest(server string, hostname Hostname, id Cont return req, nil } -// NewGetNodeContainerByIDRequest generates requests for GetNodeContainerByID -func NewGetNodeContainerByIDRequest(server string, hostname Hostname, id ContainerId) (*http.Request, error) { +// NewGetNodeContainerDockerByIDRequest generates requests for GetNodeContainerDockerByID +func NewGetNodeContainerDockerByIDRequest(server string, hostname Hostname, id DockerId) (*http.Request, error) { var err error var pathParam0 string @@ -3312,7 +3312,7 @@ func NewGetNodeContainerByIDRequest(server string, hostname Hostname, id Contain return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/%s", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/node/%s/container/docker/%s", pathParam0, pathParam1) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3330,19 +3330,19 @@ func NewGetNodeContainerByIDRequest(server string, hostname Hostname, id Contain return req, nil } -// NewPostNodeContainerExecRequest calls the generic PostNodeContainerExec builder with application/json body -func NewPostNodeContainerExecRequest(server string, hostname Hostname, id ContainerId, body PostNodeContainerExecJSONRequestBody) (*http.Request, error) { +// NewPostNodeContainerDockerExecRequest calls the generic PostNodeContainerDockerExec builder with application/json body +func NewPostNodeContainerDockerExecRequest(server string, hostname Hostname, id DockerId, body PostNodeContainerDockerExecJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostNodeContainerExecRequestWithBody(server, hostname, id, "application/json", bodyReader) + return NewPostNodeContainerDockerExecRequestWithBody(server, hostname, id, "application/json", bodyReader) } -// NewPostNodeContainerExecRequestWithBody generates requests for PostNodeContainerExec with any type of body -func NewPostNodeContainerExecRequestWithBody(server string, hostname Hostname, id ContainerId, contentType string, body io.Reader) (*http.Request, error) { +// NewPostNodeContainerDockerExecRequestWithBody generates requests for PostNodeContainerDockerExec with any type of body +func NewPostNodeContainerDockerExecRequestWithBody(server string, hostname Hostname, id DockerId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -3364,7 +3364,7 @@ func NewPostNodeContainerExecRequestWithBody(server string, hostname Hostname, i return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/%s/exec", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/node/%s/container/docker/%s/exec", pathParam0, pathParam1) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3384,8 +3384,8 @@ func NewPostNodeContainerExecRequestWithBody(server string, hostname Hostname, i return req, nil } -// NewPostNodeContainerStartRequest generates requests for PostNodeContainerStart -func NewPostNodeContainerStartRequest(server string, hostname Hostname, id ContainerId) (*http.Request, error) { +// NewPostNodeContainerDockerStartRequest generates requests for PostNodeContainerDockerStart +func NewPostNodeContainerDockerStartRequest(server string, hostname Hostname, id DockerId) (*http.Request, error) { var err error var pathParam0 string @@ -3407,7 +3407,7 @@ func NewPostNodeContainerStartRequest(server string, hostname Hostname, id Conta return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/%s/start", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/node/%s/container/docker/%s/start", pathParam0, pathParam1) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3425,19 +3425,19 @@ func NewPostNodeContainerStartRequest(server string, hostname Hostname, id Conta return req, nil } -// NewPostNodeContainerStopRequest calls the generic PostNodeContainerStop builder with application/json body -func NewPostNodeContainerStopRequest(server string, hostname Hostname, id ContainerId, body PostNodeContainerStopJSONRequestBody) (*http.Request, error) { +// NewPostNodeContainerDockerStopRequest calls the generic PostNodeContainerDockerStop builder with application/json body +func NewPostNodeContainerDockerStopRequest(server string, hostname Hostname, id DockerId, body PostNodeContainerDockerStopJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostNodeContainerStopRequestWithBody(server, hostname, id, "application/json", bodyReader) + return NewPostNodeContainerDockerStopRequestWithBody(server, hostname, id, "application/json", bodyReader) } -// NewPostNodeContainerStopRequestWithBody generates requests for PostNodeContainerStop with any type of body -func NewPostNodeContainerStopRequestWithBody(server string, hostname Hostname, id ContainerId, contentType string, body io.Reader) (*http.Request, error) { +// NewPostNodeContainerDockerStopRequestWithBody generates requests for PostNodeContainerDockerStop with any type of body +func NewPostNodeContainerDockerStopRequestWithBody(server string, hostname Hostname, id DockerId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -3459,7 +3459,7 @@ func NewPostNodeContainerStopRequestWithBody(server string, hostname Hostname, i return nil, err } - operationPath := fmt.Sprintf("/node/%s/container/%s/stop", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/node/%s/container/docker/%s/stop", pathParam0, pathParam1) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -4051,37 +4051,37 @@ type ClientWithResponsesInterface interface { PostNodeCommandShellWithResponse(ctx context.Context, hostname Hostname, body PostNodeCommandShellJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeCommandShellResponse, error) - // GetNodeContainerWithResponse request - GetNodeContainerWithResponse(ctx context.Context, hostname Hostname, params *GetNodeContainerParams, reqEditors ...RequestEditorFn) (*GetNodeContainerResponse, error) + // GetNodeContainerDockerWithResponse request + GetNodeContainerDockerWithResponse(ctx context.Context, hostname Hostname, params *GetNodeContainerDockerParams, reqEditors ...RequestEditorFn) (*GetNodeContainerDockerResponse, error) - // PostNodeContainerWithBodyWithResponse request with any body - PostNodeContainerWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerResponse, error) + // PostNodeContainerDockerWithBodyWithResponse request with any body + PostNodeContainerDockerWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerResponse, error) - PostNodeContainerWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerResponse, error) + PostNodeContainerDockerWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerDockerJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerResponse, error) - // PostNodeContainerPullWithBodyWithResponse request with any body - PostNodeContainerPullWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerPullResponse, error) + // PostNodeContainerDockerPullWithBodyWithResponse request with any body + PostNodeContainerDockerPullWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerPullResponse, error) - PostNodeContainerPullWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerPullResponse, error) + PostNodeContainerDockerPullWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerDockerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerPullResponse, error) - // DeleteNodeContainerByIDWithResponse request - DeleteNodeContainerByIDWithResponse(ctx context.Context, hostname Hostname, id ContainerId, params *DeleteNodeContainerByIDParams, reqEditors ...RequestEditorFn) (*DeleteNodeContainerByIDResponse, error) + // DeleteNodeContainerDockerByIDWithResponse request + DeleteNodeContainerDockerByIDWithResponse(ctx context.Context, hostname Hostname, id DockerId, params *DeleteNodeContainerDockerByIDParams, reqEditors ...RequestEditorFn) (*DeleteNodeContainerDockerByIDResponse, error) - // GetNodeContainerByIDWithResponse request - GetNodeContainerByIDWithResponse(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*GetNodeContainerByIDResponse, error) + // GetNodeContainerDockerByIDWithResponse request + GetNodeContainerDockerByIDWithResponse(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*GetNodeContainerDockerByIDResponse, error) - // PostNodeContainerExecWithBodyWithResponse request with any body - PostNodeContainerExecWithBodyWithResponse(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerExecResponse, error) + // PostNodeContainerDockerExecWithBodyWithResponse request with any body + PostNodeContainerDockerExecWithBodyWithResponse(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerExecResponse, error) - PostNodeContainerExecWithResponse(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerExecResponse, error) + PostNodeContainerDockerExecWithResponse(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerExecResponse, error) - // PostNodeContainerStartWithResponse request - PostNodeContainerStartWithResponse(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*PostNodeContainerStartResponse, error) + // PostNodeContainerDockerStartWithResponse request + PostNodeContainerDockerStartWithResponse(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStartResponse, error) - // PostNodeContainerStopWithBodyWithResponse request with any body - PostNodeContainerStopWithBodyWithResponse(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerStopResponse, error) + // PostNodeContainerDockerStopWithBodyWithResponse request with any body + PostNodeContainerDockerStopWithBodyWithResponse(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStopResponse, error) - PostNodeContainerStopWithResponse(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerStopResponse, error) + PostNodeContainerDockerStopWithResponse(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStopResponse, error) // GetNodeDiskWithResponse request GetNodeDiskWithResponse(ctx context.Context, hostname Hostname, reqEditors ...RequestEditorFn) (*GetNodeDiskResponse, error) @@ -4675,10 +4675,10 @@ func (r PostNodeCommandShellResponse) StatusCode() int { return 0 } -type GetNodeContainerResponse struct { +type GetNodeContainerDockerResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ContainerListCollectionResponse + JSON200 *DockerListCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4686,7 +4686,7 @@ type GetNodeContainerResponse struct { } // Status returns HTTPResponse.Status -func (r GetNodeContainerResponse) Status() string { +func (r GetNodeContainerDockerResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4694,17 +4694,17 @@ func (r GetNodeContainerResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r GetNodeContainerResponse) StatusCode() int { +func (r GetNodeContainerDockerResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type PostNodeContainerResponse struct { +type PostNodeContainerDockerResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerResultCollectionResponse + JSON202 *DockerResultCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4712,7 +4712,7 @@ type PostNodeContainerResponse struct { } // Status returns HTTPResponse.Status -func (r PostNodeContainerResponse) Status() string { +func (r PostNodeContainerDockerResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4720,17 +4720,17 @@ func (r PostNodeContainerResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostNodeContainerResponse) StatusCode() int { +func (r PostNodeContainerDockerResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type PostNodeContainerPullResponse struct { +type PostNodeContainerDockerPullResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerPullCollectionResponse + JSON202 *DockerPullCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4738,7 +4738,7 @@ type PostNodeContainerPullResponse struct { } // Status returns HTTPResponse.Status -func (r PostNodeContainerPullResponse) Status() string { +func (r PostNodeContainerDockerPullResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4746,17 +4746,17 @@ func (r PostNodeContainerPullResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostNodeContainerPullResponse) StatusCode() int { +func (r PostNodeContainerDockerPullResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type DeleteNodeContainerByIDResponse struct { +type DeleteNodeContainerDockerByIDResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerActionCollectionResponse + JSON202 *DockerActionCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4765,7 +4765,7 @@ type DeleteNodeContainerByIDResponse struct { } // Status returns HTTPResponse.Status -func (r DeleteNodeContainerByIDResponse) Status() string { +func (r DeleteNodeContainerDockerByIDResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4773,17 +4773,17 @@ func (r DeleteNodeContainerByIDResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r DeleteNodeContainerByIDResponse) StatusCode() int { +func (r DeleteNodeContainerDockerByIDResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type GetNodeContainerByIDResponse struct { +type GetNodeContainerDockerByIDResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ContainerDetailCollectionResponse + JSON200 *DockerDetailCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4792,7 +4792,7 @@ type GetNodeContainerByIDResponse struct { } // Status returns HTTPResponse.Status -func (r GetNodeContainerByIDResponse) Status() string { +func (r GetNodeContainerDockerByIDResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4800,17 +4800,17 @@ func (r GetNodeContainerByIDResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r GetNodeContainerByIDResponse) StatusCode() int { +func (r GetNodeContainerDockerByIDResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type PostNodeContainerExecResponse struct { +type PostNodeContainerDockerExecResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerExecCollectionResponse + JSON202 *DockerExecCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4819,7 +4819,7 @@ type PostNodeContainerExecResponse struct { } // Status returns HTTPResponse.Status -func (r PostNodeContainerExecResponse) Status() string { +func (r PostNodeContainerDockerExecResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4827,17 +4827,17 @@ func (r PostNodeContainerExecResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostNodeContainerExecResponse) StatusCode() int { +func (r PostNodeContainerDockerExecResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type PostNodeContainerStartResponse struct { +type PostNodeContainerDockerStartResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerActionCollectionResponse + JSON202 *DockerActionCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4846,7 +4846,7 @@ type PostNodeContainerStartResponse struct { } // Status returns HTTPResponse.Status -func (r PostNodeContainerStartResponse) Status() string { +func (r PostNodeContainerDockerStartResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4854,17 +4854,17 @@ func (r PostNodeContainerStartResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostNodeContainerStartResponse) StatusCode() int { +func (r PostNodeContainerDockerStartResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type PostNodeContainerStopResponse struct { +type PostNodeContainerDockerStopResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *ContainerActionCollectionResponse + JSON202 *DockerActionCollectionResponse JSON400 *ErrorResponse JSON401 *ErrorResponse JSON403 *ErrorResponse @@ -4873,7 +4873,7 @@ type PostNodeContainerStopResponse struct { } // Status returns HTTPResponse.Status -func (r PostNodeContainerStopResponse) Status() string { +func (r PostNodeContainerDockerStopResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -4881,7 +4881,7 @@ func (r PostNodeContainerStopResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostNodeContainerStopResponse) StatusCode() int { +func (r PostNodeContainerDockerStopResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } @@ -5409,108 +5409,108 @@ func (c *ClientWithResponses) PostNodeCommandShellWithResponse(ctx context.Conte return ParsePostNodeCommandShellResponse(rsp) } -// GetNodeContainerWithResponse request returning *GetNodeContainerResponse -func (c *ClientWithResponses) GetNodeContainerWithResponse(ctx context.Context, hostname Hostname, params *GetNodeContainerParams, reqEditors ...RequestEditorFn) (*GetNodeContainerResponse, error) { - rsp, err := c.GetNodeContainer(ctx, hostname, params, reqEditors...) +// GetNodeContainerDockerWithResponse request returning *GetNodeContainerDockerResponse +func (c *ClientWithResponses) GetNodeContainerDockerWithResponse(ctx context.Context, hostname Hostname, params *GetNodeContainerDockerParams, reqEditors ...RequestEditorFn) (*GetNodeContainerDockerResponse, error) { + rsp, err := c.GetNodeContainerDocker(ctx, hostname, params, reqEditors...) if err != nil { return nil, err } - return ParseGetNodeContainerResponse(rsp) + return ParseGetNodeContainerDockerResponse(rsp) } -// PostNodeContainerWithBodyWithResponse request with arbitrary body returning *PostNodeContainerResponse -func (c *ClientWithResponses) PostNodeContainerWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerResponse, error) { - rsp, err := c.PostNodeContainerWithBody(ctx, hostname, contentType, body, reqEditors...) +// PostNodeContainerDockerWithBodyWithResponse request with arbitrary body returning *PostNodeContainerDockerResponse +func (c *ClientWithResponses) PostNodeContainerDockerWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerResponse, error) { + rsp, err := c.PostNodeContainerDockerWithBody(ctx, hostname, contentType, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerResponse(rsp) + return ParsePostNodeContainerDockerResponse(rsp) } -func (c *ClientWithResponses) PostNodeContainerWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerResponse, error) { - rsp, err := c.PostNodeContainer(ctx, hostname, body, reqEditors...) +func (c *ClientWithResponses) PostNodeContainerDockerWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerDockerJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerResponse, error) { + rsp, err := c.PostNodeContainerDocker(ctx, hostname, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerResponse(rsp) + return ParsePostNodeContainerDockerResponse(rsp) } -// PostNodeContainerPullWithBodyWithResponse request with arbitrary body returning *PostNodeContainerPullResponse -func (c *ClientWithResponses) PostNodeContainerPullWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerPullResponse, error) { - rsp, err := c.PostNodeContainerPullWithBody(ctx, hostname, contentType, body, reqEditors...) +// PostNodeContainerDockerPullWithBodyWithResponse request with arbitrary body returning *PostNodeContainerDockerPullResponse +func (c *ClientWithResponses) PostNodeContainerDockerPullWithBodyWithResponse(ctx context.Context, hostname Hostname, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerPullResponse, error) { + rsp, err := c.PostNodeContainerDockerPullWithBody(ctx, hostname, contentType, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerPullResponse(rsp) + return ParsePostNodeContainerDockerPullResponse(rsp) } -func (c *ClientWithResponses) PostNodeContainerPullWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerPullResponse, error) { - rsp, err := c.PostNodeContainerPull(ctx, hostname, body, reqEditors...) +func (c *ClientWithResponses) PostNodeContainerDockerPullWithResponse(ctx context.Context, hostname Hostname, body PostNodeContainerDockerPullJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerPullResponse, error) { + rsp, err := c.PostNodeContainerDockerPull(ctx, hostname, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerPullResponse(rsp) + return ParsePostNodeContainerDockerPullResponse(rsp) } -// DeleteNodeContainerByIDWithResponse request returning *DeleteNodeContainerByIDResponse -func (c *ClientWithResponses) DeleteNodeContainerByIDWithResponse(ctx context.Context, hostname Hostname, id ContainerId, params *DeleteNodeContainerByIDParams, reqEditors ...RequestEditorFn) (*DeleteNodeContainerByIDResponse, error) { - rsp, err := c.DeleteNodeContainerByID(ctx, hostname, id, params, reqEditors...) +// DeleteNodeContainerDockerByIDWithResponse request returning *DeleteNodeContainerDockerByIDResponse +func (c *ClientWithResponses) DeleteNodeContainerDockerByIDWithResponse(ctx context.Context, hostname Hostname, id DockerId, params *DeleteNodeContainerDockerByIDParams, reqEditors ...RequestEditorFn) (*DeleteNodeContainerDockerByIDResponse, error) { + rsp, err := c.DeleteNodeContainerDockerByID(ctx, hostname, id, params, reqEditors...) if err != nil { return nil, err } - return ParseDeleteNodeContainerByIDResponse(rsp) + return ParseDeleteNodeContainerDockerByIDResponse(rsp) } -// GetNodeContainerByIDWithResponse request returning *GetNodeContainerByIDResponse -func (c *ClientWithResponses) GetNodeContainerByIDWithResponse(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*GetNodeContainerByIDResponse, error) { - rsp, err := c.GetNodeContainerByID(ctx, hostname, id, reqEditors...) +// GetNodeContainerDockerByIDWithResponse request returning *GetNodeContainerDockerByIDResponse +func (c *ClientWithResponses) GetNodeContainerDockerByIDWithResponse(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*GetNodeContainerDockerByIDResponse, error) { + rsp, err := c.GetNodeContainerDockerByID(ctx, hostname, id, reqEditors...) if err != nil { return nil, err } - return ParseGetNodeContainerByIDResponse(rsp) + return ParseGetNodeContainerDockerByIDResponse(rsp) } -// PostNodeContainerExecWithBodyWithResponse request with arbitrary body returning *PostNodeContainerExecResponse -func (c *ClientWithResponses) PostNodeContainerExecWithBodyWithResponse(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerExecResponse, error) { - rsp, err := c.PostNodeContainerExecWithBody(ctx, hostname, id, contentType, body, reqEditors...) +// PostNodeContainerDockerExecWithBodyWithResponse request with arbitrary body returning *PostNodeContainerDockerExecResponse +func (c *ClientWithResponses) PostNodeContainerDockerExecWithBodyWithResponse(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerExecResponse, error) { + rsp, err := c.PostNodeContainerDockerExecWithBody(ctx, hostname, id, contentType, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerExecResponse(rsp) + return ParsePostNodeContainerDockerExecResponse(rsp) } -func (c *ClientWithResponses) PostNodeContainerExecWithResponse(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerExecResponse, error) { - rsp, err := c.PostNodeContainerExec(ctx, hostname, id, body, reqEditors...) +func (c *ClientWithResponses) PostNodeContainerDockerExecWithResponse(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerExecJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerExecResponse, error) { + rsp, err := c.PostNodeContainerDockerExec(ctx, hostname, id, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerExecResponse(rsp) + return ParsePostNodeContainerDockerExecResponse(rsp) } -// PostNodeContainerStartWithResponse request returning *PostNodeContainerStartResponse -func (c *ClientWithResponses) PostNodeContainerStartWithResponse(ctx context.Context, hostname Hostname, id ContainerId, reqEditors ...RequestEditorFn) (*PostNodeContainerStartResponse, error) { - rsp, err := c.PostNodeContainerStart(ctx, hostname, id, reqEditors...) +// PostNodeContainerDockerStartWithResponse request returning *PostNodeContainerDockerStartResponse +func (c *ClientWithResponses) PostNodeContainerDockerStartWithResponse(ctx context.Context, hostname Hostname, id DockerId, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStartResponse, error) { + rsp, err := c.PostNodeContainerDockerStart(ctx, hostname, id, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerStartResponse(rsp) + return ParsePostNodeContainerDockerStartResponse(rsp) } -// PostNodeContainerStopWithBodyWithResponse request with arbitrary body returning *PostNodeContainerStopResponse -func (c *ClientWithResponses) PostNodeContainerStopWithBodyWithResponse(ctx context.Context, hostname Hostname, id ContainerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerStopResponse, error) { - rsp, err := c.PostNodeContainerStopWithBody(ctx, hostname, id, contentType, body, reqEditors...) +// PostNodeContainerDockerStopWithBodyWithResponse request with arbitrary body returning *PostNodeContainerDockerStopResponse +func (c *ClientWithResponses) PostNodeContainerDockerStopWithBodyWithResponse(ctx context.Context, hostname Hostname, id DockerId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStopResponse, error) { + rsp, err := c.PostNodeContainerDockerStopWithBody(ctx, hostname, id, contentType, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerStopResponse(rsp) + return ParsePostNodeContainerDockerStopResponse(rsp) } -func (c *ClientWithResponses) PostNodeContainerStopWithResponse(ctx context.Context, hostname Hostname, id ContainerId, body PostNodeContainerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerStopResponse, error) { - rsp, err := c.PostNodeContainerStop(ctx, hostname, id, body, reqEditors...) +func (c *ClientWithResponses) PostNodeContainerDockerStopWithResponse(ctx context.Context, hostname Hostname, id DockerId, body PostNodeContainerDockerStopJSONRequestBody, reqEditors ...RequestEditorFn) (*PostNodeContainerDockerStopResponse, error) { + rsp, err := c.PostNodeContainerDockerStop(ctx, hostname, id, body, reqEditors...) if err != nil { return nil, err } - return ParsePostNodeContainerStopResponse(rsp) + return ParsePostNodeContainerDockerStopResponse(rsp) } // GetNodeDiskWithResponse request returning *GetNodeDiskResponse @@ -6770,22 +6770,22 @@ func ParsePostNodeCommandShellResponse(rsp *http.Response) (*PostNodeCommandShel return response, nil } -// ParseGetNodeContainerResponse parses an HTTP response from a GetNodeContainerWithResponse call -func ParseGetNodeContainerResponse(rsp *http.Response) (*GetNodeContainerResponse, error) { +// ParseGetNodeContainerDockerResponse parses an HTTP response from a GetNodeContainerDockerWithResponse call +func ParseGetNodeContainerDockerResponse(rsp *http.Response) (*GetNodeContainerDockerResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetNodeContainerResponse{ + response := &GetNodeContainerDockerResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ContainerListCollectionResponse + var dest DockerListCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -6824,22 +6824,22 @@ func ParseGetNodeContainerResponse(rsp *http.Response) (*GetNodeContainerRespons return response, nil } -// ParsePostNodeContainerResponse parses an HTTP response from a PostNodeContainerWithResponse call -func ParsePostNodeContainerResponse(rsp *http.Response) (*PostNodeContainerResponse, error) { +// ParsePostNodeContainerDockerResponse parses an HTTP response from a PostNodeContainerDockerWithResponse call +func ParsePostNodeContainerDockerResponse(rsp *http.Response) (*PostNodeContainerDockerResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostNodeContainerResponse{ + response := &PostNodeContainerDockerResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerResultCollectionResponse + var dest DockerResultCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -6878,22 +6878,22 @@ func ParsePostNodeContainerResponse(rsp *http.Response) (*PostNodeContainerRespo return response, nil } -// ParsePostNodeContainerPullResponse parses an HTTP response from a PostNodeContainerPullWithResponse call -func ParsePostNodeContainerPullResponse(rsp *http.Response) (*PostNodeContainerPullResponse, error) { +// ParsePostNodeContainerDockerPullResponse parses an HTTP response from a PostNodeContainerDockerPullWithResponse call +func ParsePostNodeContainerDockerPullResponse(rsp *http.Response) (*PostNodeContainerDockerPullResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostNodeContainerPullResponse{ + response := &PostNodeContainerDockerPullResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerPullCollectionResponse + var dest DockerPullCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -6932,22 +6932,22 @@ func ParsePostNodeContainerPullResponse(rsp *http.Response) (*PostNodeContainerP return response, nil } -// ParseDeleteNodeContainerByIDResponse parses an HTTP response from a DeleteNodeContainerByIDWithResponse call -func ParseDeleteNodeContainerByIDResponse(rsp *http.Response) (*DeleteNodeContainerByIDResponse, error) { +// ParseDeleteNodeContainerDockerByIDResponse parses an HTTP response from a DeleteNodeContainerDockerByIDWithResponse call +func ParseDeleteNodeContainerDockerByIDResponse(rsp *http.Response) (*DeleteNodeContainerDockerByIDResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteNodeContainerByIDResponse{ + response := &DeleteNodeContainerDockerByIDResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerActionCollectionResponse + var dest DockerActionCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -6993,22 +6993,22 @@ func ParseDeleteNodeContainerByIDResponse(rsp *http.Response) (*DeleteNodeContai return response, nil } -// ParseGetNodeContainerByIDResponse parses an HTTP response from a GetNodeContainerByIDWithResponse call -func ParseGetNodeContainerByIDResponse(rsp *http.Response) (*GetNodeContainerByIDResponse, error) { +// ParseGetNodeContainerDockerByIDResponse parses an HTTP response from a GetNodeContainerDockerByIDWithResponse call +func ParseGetNodeContainerDockerByIDResponse(rsp *http.Response) (*GetNodeContainerDockerByIDResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetNodeContainerByIDResponse{ + response := &GetNodeContainerDockerByIDResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ContainerDetailCollectionResponse + var dest DockerDetailCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -7054,22 +7054,22 @@ func ParseGetNodeContainerByIDResponse(rsp *http.Response) (*GetNodeContainerByI return response, nil } -// ParsePostNodeContainerExecResponse parses an HTTP response from a PostNodeContainerExecWithResponse call -func ParsePostNodeContainerExecResponse(rsp *http.Response) (*PostNodeContainerExecResponse, error) { +// ParsePostNodeContainerDockerExecResponse parses an HTTP response from a PostNodeContainerDockerExecWithResponse call +func ParsePostNodeContainerDockerExecResponse(rsp *http.Response) (*PostNodeContainerDockerExecResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostNodeContainerExecResponse{ + response := &PostNodeContainerDockerExecResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerExecCollectionResponse + var dest DockerExecCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -7115,22 +7115,22 @@ func ParsePostNodeContainerExecResponse(rsp *http.Response) (*PostNodeContainerE return response, nil } -// ParsePostNodeContainerStartResponse parses an HTTP response from a PostNodeContainerStartWithResponse call -func ParsePostNodeContainerStartResponse(rsp *http.Response) (*PostNodeContainerStartResponse, error) { +// ParsePostNodeContainerDockerStartResponse parses an HTTP response from a PostNodeContainerDockerStartWithResponse call +func ParsePostNodeContainerDockerStartResponse(rsp *http.Response) (*PostNodeContainerDockerStartResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostNodeContainerStartResponse{ + response := &PostNodeContainerDockerStartResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerActionCollectionResponse + var dest DockerActionCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -7176,22 +7176,22 @@ func ParsePostNodeContainerStartResponse(rsp *http.Response) (*PostNodeContainer return response, nil } -// ParsePostNodeContainerStopResponse parses an HTTP response from a PostNodeContainerStopWithResponse call -func ParsePostNodeContainerStopResponse(rsp *http.Response) (*PostNodeContainerStopResponse, error) { +// ParsePostNodeContainerDockerStopResponse parses an HTTP response from a PostNodeContainerDockerStopWithResponse call +func ParsePostNodeContainerDockerStopResponse(rsp *http.Response) (*PostNodeContainerDockerStopResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostNodeContainerStopResponse{ + response := &PostNodeContainerDockerStopResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: - var dest ContainerActionCollectionResponse + var dest DockerActionCollectionResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } diff --git a/pkg/sdk/client/osapi.go b/pkg/sdk/client/osapi.go index 0a13e381..fc6981c4 100644 --- a/pkg/sdk/client/osapi.go +++ b/pkg/sdk/client/osapi.go @@ -66,9 +66,9 @@ type Client struct { // File provides file management operations (upload, list, get, delete). File *FileService - // Container provides container management operations (create, list, + // Docker provides Docker container management operations (create, list, // inspect, start, stop, remove, exec, pull). - Container *ContainerService + Docker *DockerService httpClient *gen.ClientWithResponses baseURL string @@ -139,7 +139,7 @@ func New( baseURL: baseURL, } c.File = &FileService{client: httpClient} - c.Container = &ContainerService{client: httpClient} + c.Docker = &DockerService{client: httpClient} return c } diff --git a/pkg/sdk/orchestrator/container_provider.go b/pkg/sdk/orchestrator/container_provider.go deleted file mode 100644 index 62a6580c..00000000 --- a/pkg/sdk/orchestrator/container_provider.go +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import ( - "context" - "encoding/json" - "fmt" - "time" -) - -// ContainerProvider provides typed access to provider operations inside -// a container via the RuntimeTarget's ExecProvider method. This runs -// `osapi provider run ` inside the container, -// so the osapi binary must be present at /osapi in the container. -// -// The SDK owns the input/output type contracts. The CLI's `provider run` -// command and this typed layer use the same serialization format. -type ContainerProvider struct { - target RuntimeTarget -} - -// NewContainerProvider creates a provider bound to a RuntimeTarget. -func NewContainerProvider( - target RuntimeTarget, -) *ContainerProvider { - return &ContainerProvider{target: target} -} - -// ── Result types ───────────────────────────────────────────────────── -// -// These mirror the internal provider result types and define the JSON -// contract between the `osapi provider run` CLI and SDK consumers. - -// HostOSInfo contains operating system information. -type HostOSInfo struct { - Distribution string `json:"Distribution"` - Version string `json:"Version"` - Changed bool `json:"changed"` -} - -// MemStats contains memory statistics in bytes. -type MemStats struct { - Total uint64 `json:"Total"` - Available uint64 `json:"Available"` - Free uint64 `json:"Free"` - Cached uint64 `json:"Cached"` - Changed bool `json:"changed"` -} - -// LoadStats contains load average statistics. -type LoadStats struct { - Load1 float32 `json:"Load1"` - Load5 float32 `json:"Load5"` - Load15 float32 `json:"Load15"` - Changed bool `json:"changed"` -} - -// DiskUsage contains disk usage statistics for a single mount point. -type DiskUsage struct { - Name string `json:"Name"` - Total uint64 `json:"Total"` - Used uint64 `json:"Used"` - Free uint64 `json:"Free"` - Changed bool `json:"changed"` -} - -// CommandResult contains the output of a command execution. -type CommandResult struct { - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` - ExitCode int `json:"exit_code"` - DurationMs int64 `json:"duration_ms"` - Changed bool `json:"changed"` -} - -// ── Input types ────────────────────────────────────────────────────── -// -// These mirror the internal provider param types. - -// ExecParams contains parameters for direct command execution. -type ExecParams struct { - Command string `json:"command"` - Args []string `json:"args,omitempty"` - Cwd string `json:"cwd,omitempty"` - Timeout int `json:"timeout,omitempty"` -} - -// ShellParams contains parameters for shell command execution. -type ShellParams struct { - Command string `json:"command"` - Cwd string `json:"cwd,omitempty"` - Timeout int `json:"timeout,omitempty"` -} - -// PingParams contains parameters for the ping operation. -type PingParams struct { - Address string `json:"address"` -} - -// PingResult contains the result of a ping operation. -type PingResult struct { - PacketsSent int `json:"PacketsSent"` - PacketsReceived int `json:"PacketsReceived"` - PacketLoss float64 `json:"PacketLoss"` - MinRTT time.Duration `json:"MinRTT"` - AvgRTT time.Duration `json:"AvgRTT"` - MaxRTT time.Duration `json:"MaxRTT"` - Changed bool `json:"changed"` -} - -// DNSGetParams contains parameters for DNS configuration retrieval. -type DNSGetParams struct { - InterfaceName string `json:"interface_name"` -} - -// DNSGetResult contains DNS configuration. -type DNSGetResult struct { - DNSServers []string `json:"DNSServers"` - SearchDomains []string `json:"SearchDomains"` -} - -// DNSUpdateParams contains parameters for DNS configuration update. -type DNSUpdateParams struct { - Servers []string `json:"servers"` - SearchDomains []string `json:"search_domains"` - InterfaceName string `json:"interface_name"` -} - -// DNSUpdateResult contains the result of a DNS update operation. -type DNSUpdateResult struct { - Changed bool `json:"changed"` -} - -// ── Helper ─────────────────────────────────────────────────────────── - -// run executes a provider operation and unmarshals the JSON result. -func run[T any]( - ctx context.Context, - cp *ContainerProvider, - provider string, - operation string, - params any, -) (*T, error) { - var data []byte - if params != nil { - var err error - data, err = json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("marshal %s/%s params: %w", provider, operation, err) - } - } - - out, err := cp.target.ExecProvider(ctx, provider, operation, data) - if err != nil { - return nil, err - } - - var result T - if err := json.Unmarshal(out, &result); err != nil { - return nil, fmt.Errorf("unmarshal %s/%s result: %w", provider, operation, err) - } - - return &result, nil -} - -// runScalar executes a provider operation that returns a JSON scalar -// (string, int, float). -func runScalar[T any]( - ctx context.Context, - cp *ContainerProvider, - provider string, - operation string, -) (T, error) { - var zero T - - out, err := cp.target.ExecProvider(ctx, provider, operation, nil) - if err != nil { - return zero, err - } - - var result T - if err := json.Unmarshal(out, &result); err != nil { - return zero, fmt.Errorf("unmarshal %s/%s result: %w", provider, operation, err) - } - - return result, nil -} - -// ── Host operations ────────────────────────────────────────────────── - -// GetHostname returns the container's hostname. -func (cp *ContainerProvider) GetHostname( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-hostname") -} - -// GetOSInfo returns the container's OS distribution and version. -func (cp *ContainerProvider) GetOSInfo( - ctx context.Context, -) (*HostOSInfo, error) { - return run[HostOSInfo](ctx, cp, "host", "get-os-info", nil) -} - -// GetArchitecture returns the CPU architecture (e.g., x86_64, aarch64). -func (cp *ContainerProvider) GetArchitecture( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-architecture") -} - -// GetKernelVersion returns the kernel version string. -func (cp *ContainerProvider) GetKernelVersion( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-kernel-version") -} - -// GetUptime returns the system uptime as a duration. -func (cp *ContainerProvider) GetUptime( - ctx context.Context, -) (time.Duration, error) { - return runScalar[time.Duration](ctx, cp, "host", "get-uptime") -} - -// GetFQDN returns the fully qualified domain name. -func (cp *ContainerProvider) GetFQDN( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-fqdn") -} - -// GetCPUCount returns the number of logical CPUs. -func (cp *ContainerProvider) GetCPUCount( - ctx context.Context, -) (int, error) { - return runScalar[int](ctx, cp, "host", "get-cpu-count") -} - -// GetServiceManager returns the system service manager (e.g., systemd). -func (cp *ContainerProvider) GetServiceManager( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-service-manager") -} - -// GetPackageManager returns the system package manager (e.g., apt, dnf). -func (cp *ContainerProvider) GetPackageManager( - ctx context.Context, -) (string, error) { - return runScalar[string](ctx, cp, "host", "get-package-manager") -} - -// ── Memory operations ──────────────────────────────────────────────── - -// GetMemStats returns memory statistics. -func (cp *ContainerProvider) GetMemStats( - ctx context.Context, -) (*MemStats, error) { - return run[MemStats](ctx, cp, "mem", "get-stats", nil) -} - -// ── Load operations ────────────────────────────────────────────────── - -// GetLoadStats returns load average statistics. -func (cp *ContainerProvider) GetLoadStats( - ctx context.Context, -) (*LoadStats, error) { - return run[LoadStats](ctx, cp, "load", "get-average-stats", nil) -} - -// ── Disk operations ────────────────────────────────────────────────── - -// GetDiskUsage returns disk usage statistics for all local mounts. -func (cp *ContainerProvider) GetDiskUsage( - ctx context.Context, -) ([]DiskUsage, error) { - out, err := cp.target.ExecProvider(ctx, "disk", "get-local-usage-stats", nil) - if err != nil { - return nil, err - } - - var result []DiskUsage - if err := json.Unmarshal(out, &result); err != nil { - return nil, fmt.Errorf("unmarshal disk/get-local-usage-stats result: %w", err) - } - - return result, nil -} - -// ── Command operations ─────────────────────────────────────────────── - -// Exec runs a command inside the container via the command provider. -func (cp *ContainerProvider) Exec( - ctx context.Context, - params ExecParams, -) (*CommandResult, error) { - return run[CommandResult](ctx, cp, "command", "exec", ¶ms) -} - -// Shell runs a shell command inside the container via the command provider. -func (cp *ContainerProvider) Shell( - ctx context.Context, - params ShellParams, -) (*CommandResult, error) { - return run[CommandResult](ctx, cp, "command", "shell", ¶ms) -} - -// ── Ping operations ────────────────────────────────────────────────── - -// Ping pings an address from inside the container. -func (cp *ContainerProvider) Ping( - ctx context.Context, - address string, -) (*PingResult, error) { - return run[PingResult](ctx, cp, "ping", "do", &PingParams{Address: address}) -} - -// ── DNS operations ─────────────────────────────────────────────────── - -// GetDNS returns the DNS configuration for the given interface. -func (cp *ContainerProvider) GetDNS( - ctx context.Context, - interfaceName string, -) (*DNSGetResult, error) { - return run[DNSGetResult](ctx, cp, "dns", "get-resolv-conf", &DNSGetParams{ - InterfaceName: interfaceName, - }) -} - -// UpdateDNS updates the DNS configuration for the given interface. -func (cp *ContainerProvider) UpdateDNS( - ctx context.Context, - params DNSUpdateParams, -) (*DNSUpdateResult, error) { - return run[DNSUpdateResult](ctx, cp, "dns", "update-resolv-conf", ¶ms) -} diff --git a/pkg/sdk/orchestrator/container_provider_public_test.go b/pkg/sdk/orchestrator/container_provider_public_test.go deleted file mode 100644 index d695c3b9..00000000 --- a/pkg/sdk/orchestrator/container_provider_public_test.go +++ /dev/null @@ -1,828 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator_test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/pkg/sdk/orchestrator" -) - -// mockTarget implements RuntimeTarget for testing. -type mockTarget struct { - responses map[string][]byte - errors map[string]error - lastData []byte -} - -func (m *mockTarget) Name() string { - return "test-container" -} - -func (m *mockTarget) Runtime() string { - return "docker" -} - -func (m *mockTarget) ExecProvider( - _ context.Context, - provider string, - operation string, - data []byte, -) ([]byte, error) { - m.lastData = data - key := provider + "/" + operation - - if err, ok := m.errors[key]; ok { - return nil, err - } - - if resp, ok := m.responses[key]; ok { - return resp, nil - } - - return nil, fmt.Errorf("unexpected call: %s", key) -} - -type ContainerProviderPublicTestSuite struct { - suite.Suite -} - -func TestContainerProviderPublicTestSuite(t *testing.T) { - suite.Run(t, new(ContainerProviderPublicTestSuite)) -} - -func (suite *ContainerProviderPublicTestSuite) TestGetHostname() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns hostname", - response: []byte(`"my-container"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "my-container", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "connection refused") - }, - }, - { - name: "unmarshal error", - response: []byte(`not-json`), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "unmarshal") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-hostname": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-hostname"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetHostname(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetOSInfo() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result *orchestrator.HostOSInfo, err error) - }{ - { - name: "returns os info", - response: []byte(`{"Distribution":"ubuntu","Version":"24.04"}`), - validateFunc: func(result *orchestrator.HostOSInfo, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "ubuntu", result.Distribution) - assert.Equal(suite.T(), "24.04", result.Version) - }, - }, - { - name: "exec error", - err: fmt.Errorf("timeout"), - validateFunc: func(_ *orchestrator.HostOSInfo, err error) { - assert.Error(suite.T(), err) - }, - }, - { - name: "unmarshal error", - response: []byte(`{bad`), - validateFunc: func(_ *orchestrator.HostOSInfo, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "unmarshal") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-os-info": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-os-info"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetOSInfo(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetMemStats() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result *orchestrator.MemStats, err error) - }{ - { - name: "parses memory stats", - response: []byte( - `{"Total":8192000000,"Available":4096000000,"Free":2048000000,"Cached":1024000000}`, - ), - validateFunc: func(result *orchestrator.MemStats, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), uint64(8192000000), result.Total) - assert.Equal(suite.T(), uint64(4096000000), result.Available) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"mem/get-stats": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["mem/get-stats"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetMemStats(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetLoadStats() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result *orchestrator.LoadStats, err error) - }{ - { - name: "parses load stats", - response: []byte(`{"Load1":0.5,"Load5":0.75,"Load15":1.0}`), - validateFunc: func(result *orchestrator.LoadStats, err error) { - assert.NoError(suite.T(), err) - assert.InDelta(suite.T(), float32(0.5), result.Load1, 0.01) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"load/get-average-stats": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["load/get-average-stats"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetLoadStats(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestExec() { - tests := []struct { - name string - params orchestrator.ExecParams - response []byte - err error - validateFunc func(result *orchestrator.CommandResult, err error, lastData []byte) - }{ - { - name: "runs command with changed true", - params: orchestrator.ExecParams{ - Command: "uname", - Args: []string{"-a"}, - }, - response: []byte( - `{"stdout":"Linux container 5.15.0\n","stderr":"","exit_code":0,"duration_ms":5,"changed":true}`, - ), - validateFunc: func(result *orchestrator.CommandResult, err error, lastData []byte) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "Linux container 5.15.0\n", result.Stdout) - assert.True(suite.T(), result.Changed) - - var sent orchestrator.ExecParams - _ = json.Unmarshal(lastData, &sent) - assert.Equal(suite.T(), "uname", sent.Command) - assert.Equal(suite.T(), []string{"-a"}, sent.Args) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"command/exec": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["command/exec"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.Exec(context.Background(), tc.params) - tc.validateFunc(result, err, target.lastData) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestShell() { - tests := []struct { - name string - command string - response []byte - err error - validateFunc func(result *orchestrator.CommandResult, err error) - }{ - { - name: "runs shell command", - command: "echo hello", - response: []byte(`{"stdout":"hello\n","stderr":"","exit_code":0,"duration_ms":2}`), - validateFunc: func(result *orchestrator.CommandResult, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "hello\n", result.Stdout) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"command/shell": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["command/shell"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.Shell(context.Background(), orchestrator.ShellParams{ - Command: tc.command, - }) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestPing() { - tests := []struct { - name string - address string - response []byte - err error - validateFunc func(result *orchestrator.PingResult, err error, lastData []byte) - }{ - { - name: "pings address", - address: "8.8.8.8", - response: []byte(`{"PacketsSent":3,"PacketsReceived":3,"PacketLoss":0}`), - validateFunc: func(result *orchestrator.PingResult, err error, lastData []byte) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), 3, result.PacketsSent) - - var sent orchestrator.PingParams - _ = json.Unmarshal(lastData, &sent) - assert.Equal(suite.T(), "8.8.8.8", sent.Address) - }, - }, - { - name: "exec error", - address: "8.8.8.8", - err: fmt.Errorf("timeout"), - validateFunc: func(_ *orchestrator.PingResult, err error, _ []byte) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"ping/do": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["ping/do"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.Ping(context.Background(), tc.address) - tc.validateFunc(result, err, target.lastData) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetArchitecture() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns architecture", - response: []byte(`"x86_64"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "x86_64", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-architecture": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-architecture"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetArchitecture(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetKernelVersion() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns kernel version", - response: []byte(`"5.15.0-91-generic"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "5.15.0-91-generic", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-kernel-version": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-kernel-version"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetKernelVersion(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetUptime() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result time.Duration, err error) - }{ - { - name: "returns uptime duration", - response: []byte(`3600000000000`), - validateFunc: func(result time.Duration, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), time.Hour, result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ time.Duration, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-uptime": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-uptime"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetUptime(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetFQDN() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns fqdn", - response: []byte(`"web-01.example.com"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "web-01.example.com", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-fqdn": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-fqdn"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetFQDN(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetCPUCount() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result int, err error) - }{ - { - name: "returns cpu count", - response: []byte(`4`), - validateFunc: func(result int, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), 4, result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ int, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-cpu-count": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-cpu-count"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetCPUCount(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetServiceManager() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns service manager", - response: []byte(`"systemd"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "systemd", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-service-manager": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-service-manager"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetServiceManager(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetPackageManager() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result string, err error) - }{ - { - name: "returns package manager", - response: []byte(`"apt"`), - validateFunc: func(result string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "apt", result) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"host/get-package-manager": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["host/get-package-manager"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetPackageManager(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetDiskUsage() { - tests := []struct { - name string - response []byte - err error - validateFunc func(result []orchestrator.DiskUsage, err error) - }{ - { - name: "returns disk usage", - response: []byte( - `[{"Name":"/","Total":50000000000,"Used":25000000000,"Free":25000000000}]`, - ), - validateFunc: func(result []orchestrator.DiskUsage, err error) { - assert.NoError(suite.T(), err) - assert.Len(suite.T(), result, 1) - assert.Equal(suite.T(), "/", result[0].Name) - assert.Equal(suite.T(), uint64(50000000000), result[0].Total) - }, - }, - { - name: "exec error", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ []orchestrator.DiskUsage, err error) { - assert.Error(suite.T(), err) - }, - }, - { - name: "unmarshal error", - response: []byte(`not-json`), - validateFunc: func(_ []orchestrator.DiskUsage, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "unmarshal") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"disk/get-local-usage-stats": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["disk/get-local-usage-stats"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetDiskUsage(context.Background()) - tc.validateFunc(result, err) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestGetDNS() { - tests := []struct { - name string - iface string - response []byte - err error - validateFunc func(result *orchestrator.DNSGetResult, err error, lastData []byte) - }{ - { - name: "returns dns config", - iface: "eth0", - response: []byte( - `{"DNSServers":["8.8.8.8","8.8.4.4"],"SearchDomains":["example.com"]}`, - ), - validateFunc: func(result *orchestrator.DNSGetResult, err error, lastData []byte) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), []string{"8.8.8.8", "8.8.4.4"}, result.DNSServers) - assert.Equal(suite.T(), []string{"example.com"}, result.SearchDomains) - - var sent orchestrator.DNSGetParams - _ = json.Unmarshal(lastData, &sent) - assert.Equal(suite.T(), "eth0", sent.InterfaceName) - }, - }, - { - name: "exec error", - iface: "eth0", - err: fmt.Errorf("connection refused"), - validateFunc: func(_ *orchestrator.DNSGetResult, err error, _ []byte) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"dns/get-resolv-conf": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["dns/get-resolv-conf"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.GetDNS(context.Background(), tc.iface) - tc.validateFunc(result, err, target.lastData) - }) - } -} - -func (suite *ContainerProviderPublicTestSuite) TestUpdateDNS() { - tests := []struct { - name string - params orchestrator.DNSUpdateParams - response []byte - err error - validateFunc func(result *orchestrator.DNSUpdateResult, err error, lastData []byte) - }{ - { - name: "updates dns config", - params: orchestrator.DNSUpdateParams{ - Servers: []string{"8.8.8.8"}, - SearchDomains: []string{"example.com"}, - InterfaceName: "eth0", - }, - response: []byte(`{"changed":true}`), - validateFunc: func(result *orchestrator.DNSUpdateResult, err error, lastData []byte) { - assert.NoError(suite.T(), err) - assert.True(suite.T(), result.Changed) - - var sent orchestrator.DNSUpdateParams - _ = json.Unmarshal(lastData, &sent) - assert.Equal(suite.T(), []string{"8.8.8.8"}, sent.Servers) - assert.Equal(suite.T(), "eth0", sent.InterfaceName) - }, - }, - { - name: "exec error", - params: orchestrator.DNSUpdateParams{ - Servers: []string{"8.8.8.8"}, - InterfaceName: "eth0", - }, - err: fmt.Errorf("connection refused"), - validateFunc: func(_ *orchestrator.DNSUpdateResult, err error, _ []byte) { - assert.Error(suite.T(), err) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &mockTarget{ - responses: map[string][]byte{"dns/update-resolv-conf": tc.response}, - errors: map[string]error{}, - } - if tc.err != nil { - target.errors["dns/update-resolv-conf"] = tc.err - } - - cp := orchestrator.NewContainerProvider(target) - result, err := cp.UpdateDNS(context.Background(), tc.params) - tc.validateFunc(result, err, target.lastData) - }) - } -} diff --git a/pkg/sdk/orchestrator/container_provider_test.go b/pkg/sdk/orchestrator/container_provider_test.go deleted file mode 100644 index 49f20717..00000000 --- a/pkg/sdk/orchestrator/container_provider_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type ContainerProviderTestSuite struct { - suite.Suite -} - -func TestContainerProviderTestSuite(t *testing.T) { - suite.Run(t, new(ContainerProviderTestSuite)) -} - -func (suite *ContainerProviderTestSuite) TestRunMarshalError() { - tests := []struct { - name string - params any - validateFunc func(err error) - }{ - { - name: "returns error when params cannot be marshaled", - params: make(chan int), - validateFunc: func(err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "marshal") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - target := &DockerTarget{ - name: "test", - execFn: func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return `{}`, "", 0, nil - }, - } - - cp := NewContainerProvider(target) - _, err := run[CommandResult]( - context.Background(), - cp, - "test", - "op", - tc.params, - ) - tc.validateFunc(err) - }) - } -} diff --git a/pkg/sdk/orchestrator/deploy.go b/pkg/sdk/orchestrator/deploy.go deleted file mode 100644 index 72fc2226..00000000 --- a/pkg/sdk/orchestrator/deploy.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" -) - -const ( - defaultGitHubOwner = "retr0h" - defaultGitHubRepo = "osapi" -) - -// releaseAsset represents a single asset in a GitHub release. -type releaseAsset struct { - Name string `json:"name"` - BrowserDownloadURL string `json:"browser_download_url"` -} - -// githubRelease represents the GitHub API response for a release. -type githubRelease struct { - TagName string `json:"tag_name"` - Assets []releaseAsset `json:"assets"` -} - -// httpClient allows injection of a custom HTTP client for testing. -var httpClient = http.DefaultClient - -// resolveLatestBinaryURL queries the GitHub API for the latest release -// of the osapi repository and returns the download URL for the binary -// matching the given OS and architecture. -func resolveLatestBinaryURL( - ctx context.Context, - goos string, - goarch string, -) (string, error) { - apiURL := fmt.Sprintf( - "https://api.github.com/repos/%s/%s/releases/latest", - defaultGitHubOwner, - defaultGitHubRepo, - ) - - return resolveFromURL(ctx, apiURL, goos, goarch) -} - -// resolveFromURL fetches a GitHub release JSON from the given URL and -// returns the download URL for the binary matching goos/goarch. -func resolveFromURL( - ctx context.Context, - apiURL string, - goos string, - goarch string, -) (string, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil) - if err != nil { - return "", fmt.Errorf("create request: %w", err) - } - - req.Header.Set("Accept", "application/vnd.github+json") - - resp, err := httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("query GitHub releases: %w", err) - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf( - "GitHub releases returned %d (no release published?)", - resp.StatusCode, - ) - } - - var release githubRelease - if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { - return "", fmt.Errorf("decode release response: %w", err) - } - - return matchAsset(release.Assets, goos, goarch) -} - -// matchAsset finds the download URL for the binary matching the given -// OS and architecture in the release assets. -func matchAsset( - assets []releaseAsset, - goos string, - goarch string, -) (string, error) { - suffix := fmt.Sprintf("_%s_%s", goos, goarch) - - for _, a := range assets { - if strings.HasSuffix(a.Name, suffix) { - return a.BrowserDownloadURL, nil - } - } - - return "", fmt.Errorf( - "no osapi binary found for %s/%s in release assets", - goos, - goarch, - ) -} - -// deployScript returns a shell script that ensures curl is available -// and downloads the osapi binary to /osapi inside the container. -func deployScript( - binaryURL string, -) string { - return fmt.Sprintf( - `command -v curl >/dev/null 2>&1 || `+ - `(apt-get update -qq && apt-get install -y -qq ca-certificates curl >/dev/null 2>&1) && `+ - `curl -fsSL '%s' -o /osapi && chmod +x /osapi`, - binaryURL, - ) -} diff --git a/pkg/sdk/orchestrator/deploy_public_test.go b/pkg/sdk/orchestrator/deploy_public_test.go deleted file mode 100644 index f5d61a2b..00000000 --- a/pkg/sdk/orchestrator/deploy_public_test.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/pkg/sdk/orchestrator" -) - -type DeployPublicTestSuite struct { - suite.Suite -} - -func TestDeployPublicTestSuite(t *testing.T) { - suite.Run(t, new(DeployPublicTestSuite)) -} - -func (suite *DeployPublicTestSuite) TestPrepare() { - tests := []struct { - name string - binaryURL string - skipPrepare bool - callTwice bool - execStdout string - execStderr string - execCode int - execErr error - validateFunc func(err error, capturedCmds [][]string) - }{ - { - name: "downloads binary from custom URL", - binaryURL: "https://example.com/osapi-linux", - validateFunc: func(err error, capturedCmds [][]string) { - assert.NoError(suite.T(), err) - assert.Len(suite.T(), capturedCmds, 1) - assert.Equal(suite.T(), "sh", capturedCmds[0][0]) - assert.Equal(suite.T(), "-c", capturedCmds[0][1]) - assert.Contains(suite.T(), capturedCmds[0][2], "https://example.com/osapi-linux") - assert.Contains(suite.T(), capturedCmds[0][2], "curl") - assert.Contains(suite.T(), capturedCmds[0][2], "chmod +x /osapi") - }, - }, - { - name: "returns error on non-zero exit", - binaryURL: "https://example.com/osapi-linux", - execStderr: "curl: (22) 404 Not Found", - execCode: 22, - validateFunc: func(err error, _ [][]string) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "deploy osapi binary (exit 22)") - assert.Contains(suite.T(), err.Error(), "404 Not Found") - }, - }, - { - name: "returns error on exec failure", - binaryURL: "https://example.com/osapi-linux", - execErr: assert.AnError, - validateFunc: func(err error, _ [][]string) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "deploy osapi binary") - }, - }, - { - name: "skips preparation when configured", - skipPrepare: true, - validateFunc: func(err error, capturedCmds [][]string) { - assert.NoError(suite.T(), err) - assert.Empty(suite.T(), capturedCmds) - }, - }, - { - name: "executes preparation only once across multiple calls", - binaryURL: "https://example.com/osapi", - callTwice: true, - validateFunc: func(err error, capturedCmds [][]string) { - assert.NoError(suite.T(), err) - assert.Len(suite.T(), capturedCmds, 1) - }, - }, - { - name: "returns error when GitHub release resolution fails", - validateFunc: func(err error, _ [][]string) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "resolve osapi binary") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - var capturedCmds [][]string - - execFn := func( - _ context.Context, - _ string, - cmd []string, - ) (string, string, int, error) { - capturedCmds = append(capturedCmds, cmd) - if tc.execErr != nil { - return "", "", -1, tc.execErr - } - - return tc.execStdout, tc.execStderr, tc.execCode, nil - } - - target := orchestrator.NewDockerTarget("web", "ubuntu:24.04", execFn) - if tc.binaryURL != "" { - target.SetBinaryURL(tc.binaryURL) - } - if tc.skipPrepare { - target.SetSkipPrepare(true) - } - - if tc.callTwice { - _ = target.Prepare(context.Background()) - } - - err := target.Prepare(context.Background()) - tc.validateFunc(err, capturedCmds) - }) - } -} - -func (suite *DeployPublicTestSuite) TestExecProvider() { - tests := []struct { - name string - binaryURL string - validateFunc func(result []byte, err error, cmds [][]string) - }{ - { - name: "returns prepare error", - validateFunc: func(result []byte, err error, _ [][]string) { - assert.Error(suite.T(), err) - assert.Nil(suite.T(), result) - assert.Contains(suite.T(), err.Error(), "resolve osapi binary") - }, - }, - { - name: "triggers automatic preparation", - binaryURL: "https://example.com/osapi", - validateFunc: func(_ []byte, err error, cmds [][]string) { - assert.NoError(suite.T(), err) - // First call is the deploy script, second is the provider command. - assert.Len(suite.T(), cmds, 2) - assert.Equal(suite.T(), "sh", cmds[0][0]) - assert.Contains(suite.T(), cmds[0][2], "curl") - assert.Equal(suite.T(), "/osapi", cmds[1][0]) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - var cmds [][]string - - execFn := func( - _ context.Context, - _ string, - cmd []string, - ) (string, string, int, error) { - cmds = append(cmds, cmd) - - return `"ok"`, "", 0, nil - } - - target := orchestrator.NewDockerTarget("web", "ubuntu:24.04", execFn) - if tc.binaryURL != "" { - target.SetBinaryURL(tc.binaryURL) - } - - result, err := target.ExecProvider( - context.Background(), - "host", - "get-hostname", - nil, - ) - tc.validateFunc(result, err, cmds) - }) - } -} diff --git a/pkg/sdk/orchestrator/deploy_test.go b/pkg/sdk/orchestrator/deploy_test.go deleted file mode 100644 index 2d89c2cf..00000000 --- a/pkg/sdk/orchestrator/deploy_test.go +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type DeployTestSuite struct { - suite.Suite -} - -func TestDeployTestSuite(t *testing.T) { - suite.Run(t, new(DeployTestSuite)) -} - -func (suite *DeployTestSuite) TestMatchAsset() { - tests := []struct { - name string - assets []releaseAsset - goos string - goarch string - validateFunc func(url string, err error) - }{ - { - name: "matches linux amd64 asset", - assets: []releaseAsset{ - {Name: "osapi_1.0.0_darwin_all", BrowserDownloadURL: "https://example.com/darwin"}, - { - Name: "osapi_1.0.0_linux_amd64", - BrowserDownloadURL: "https://example.com/linux_amd64", - }, - { - Name: "osapi_1.0.0_linux_arm64", - BrowserDownloadURL: "https://example.com/linux_arm64", - }, - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(url string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "https://example.com/linux_amd64", url) - }, - }, - { - name: "matches linux arm64 asset", - assets: []releaseAsset{ - { - Name: "osapi_1.0.0_linux_amd64", - BrowserDownloadURL: "https://example.com/linux_amd64", - }, - { - Name: "osapi_1.0.0_linux_arm64", - BrowserDownloadURL: "https://example.com/linux_arm64", - }, - }, - goos: "linux", - goarch: "arm64", - validateFunc: func(url string, err error) { - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "https://example.com/linux_arm64", url) - }, - }, - { - name: "returns error when no matching asset", - assets: []releaseAsset{ - {Name: "osapi_1.0.0_darwin_all", BrowserDownloadURL: "https://example.com/darwin"}, - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "no osapi binary found for linux/amd64") - }, - }, - { - name: "returns error when no assets", - assets: nil, - goos: "linux", - goarch: "amd64", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "no osapi binary found") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - url, err := matchAsset(tc.assets, tc.goos, tc.goarch) - tc.validateFunc(url, err) - }) - } -} - -func (suite *DeployTestSuite) TestResolveFromURL() { - tests := []struct { - name string - handler http.HandlerFunc - goos string - goarch string - validateFunc func(url string, err error) - }{ - { - name: "resolves URL from release response", - handler: func(w http.ResponseWriter, _ *http.Request) { - release := githubRelease{ - TagName: "v1.0.0", - Assets: []releaseAsset{ - { - Name: "osapi_1.0.0_linux_amd64", - BrowserDownloadURL: "https://github.com/retr0h/osapi/releases/download/v1.0.0/osapi_1.0.0_linux_amd64", - }, - }, - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(release) - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(url string, err error) { - assert.NoError(suite.T(), err) - assert.Contains(suite.T(), url, "osapi_1.0.0_linux_amd64") - }, - }, - { - name: "returns error on 404", - handler: func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusNotFound) - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "404") - }, - }, - { - name: "returns error on invalid JSON", - handler: func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write([]byte(`{invalid`)) - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "decode") - }, - }, - { - name: "returns error when no matching asset in release", - handler: func(w http.ResponseWriter, _ *http.Request) { - release := githubRelease{ - TagName: "v1.0.0", - Assets: []releaseAsset{ - { - Name: "osapi_1.0.0_darwin_all", - BrowserDownloadURL: "https://example.com/darwin", - }, - }, - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(release) - }, - goos: "linux", - goarch: "amd64", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "no osapi binary found") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - server := httptest.NewServer(tc.handler) - defer server.Close() - - url, err := resolveFromURL( - context.Background(), - server.URL, - tc.goos, - tc.goarch, - ) - tc.validateFunc(url, err) - }) - } -} - -func (suite *DeployTestSuite) TestResolveLatestBinaryURL() { - tests := []struct { - name string - handler http.HandlerFunc - validateFunc func(url string, err error) - }{ - { - name: "resolves from GitHub API", - handler: func(w http.ResponseWriter, _ *http.Request) { - release := githubRelease{ - TagName: "v1.0.0", - Assets: []releaseAsset{ - { - Name: "osapi_1.0.0_linux_arm64", - BrowserDownloadURL: "https://example.com/arm64", - }, - { - Name: "osapi_1.0.0_linux_amd64", - BrowserDownloadURL: "https://example.com/amd64", - }, - }, - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(release) - }, - validateFunc: func(url string, err error) { - assert.NoError(suite.T(), err) - assert.NotEmpty(suite.T(), url) - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - server := httptest.NewServer(tc.handler) - defer server.Close() - - original := httpClient - // Create a client that redirects all requests to the test server. - httpClient = &http.Client{ - Transport: &rewriteTransport{ - base: server.Client().Transport, - url: server.URL, - }, - } - defer func() { httpClient = original }() - - url, err := resolveLatestBinaryURL( - context.Background(), - "linux", - "amd64", - ) - tc.validateFunc(url, err) - }) - } -} - -// rewriteTransport redirects all requests to a test server URL. -type rewriteTransport struct { - base http.RoundTripper - url string -} - -func (t *rewriteTransport) RoundTrip( - req *http.Request, -) (*http.Response, error) { - req = req.Clone(req.Context()) - req.URL.Scheme = "http" - req.URL.Host = t.url[len("http://"):] - - return t.base.RoundTrip(req) -} - -func (suite *DeployTestSuite) TestResolveFromURLHTTPError() { - tests := []struct { - name string - apiURL string - validateFunc func(url string, err error) - }{ - { - name: "returns error on connection failure", - apiURL: "http://127.0.0.1:0/invalid", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "query GitHub releases") - }, - }, - { - name: "returns error on invalid URL", - apiURL: "http://invalid\x00url", - validateFunc: func(_ string, err error) { - assert.Error(suite.T(), err) - assert.Contains(suite.T(), err.Error(), "create request") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - url, err := resolveFromURL( - context.Background(), - tc.apiURL, - "linux", - "amd64", - ) - tc.validateFunc(url, err) - }) - } -} - -func (suite *DeployTestSuite) TestDeployScript() { - tests := []struct { - name string - binaryURL string - validateFunc func(script string) - }{ - { - name: "generates valid download script", - binaryURL: "https://example.com/osapi", - validateFunc: func(script string) { - assert.Contains(suite.T(), script, "curl") - assert.Contains(suite.T(), script, "https://example.com/osapi") - assert.Contains(suite.T(), script, "-o /osapi") - assert.Contains(suite.T(), script, "chmod +x /osapi") - }, - }, - } - - for _, tc := range tests { - suite.Run(tc.name, func() { - script := deployScript(tc.binaryURL) - tc.validateFunc(script) - }) - } -} diff --git a/pkg/sdk/orchestrator/docker.go b/pkg/sdk/orchestrator/docker.go new file mode 100644 index 00000000..7c1af2a3 --- /dev/null +++ b/pkg/sdk/orchestrator/docker.go @@ -0,0 +1,249 @@ +package orchestrator + +import ( + "context" + + osapiclient "github.com/retr0h/osapi/pkg/sdk/client" + "github.com/retr0h/osapi/pkg/sdk/client/gen" +) + +// DockerPull creates a task that pulls a Docker image on the target host. +func (p *Plan) DockerPull( + name string, + target string, + image string, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Pull(ctx, target, gen.DockerPullRequest{ + Image: image, + }) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "image_id": r.ImageID, + "tag": r.Tag, + "size": r.Size, + }, + }, nil + }) +} + +// DockerCreate creates a task that creates a Docker container on the +// target host. +func (p *Plan) DockerCreate( + name string, + target string, + body gen.DockerCreateRequest, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Create(ctx, target, body) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "id": r.ID, + "name": r.Name, + "image": r.Image, + "state": r.State, + }, + }, nil + }) +} + +// DockerStart creates a task that starts a Docker container on the +// target host. +func (p *Plan) DockerStart( + name string, + target string, + id string, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Start(ctx, target, id) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "id": r.ID, + "message": r.Message, + }, + }, nil + }) +} + +// DockerStop creates a task that stops a Docker container on the +// target host. +func (p *Plan) DockerStop( + name string, + target string, + id string, + body gen.DockerStopRequest, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Stop(ctx, target, id, body) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "id": r.ID, + "message": r.Message, + }, + }, nil + }) +} + +// DockerRemove creates a task that removes a Docker container from the +// target host. +func (p *Plan) DockerRemove( + name string, + target string, + id string, + params *gen.DeleteNodeContainerDockerByIDParams, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Remove(ctx, target, id, params) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "id": r.ID, + "message": r.Message, + }, + }, nil + }) +} + +// DockerExec creates a task that executes a command in a Docker +// container. +func (p *Plan) DockerExec( + name string, + target string, + id string, + body gen.DockerExecRequest, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Exec(ctx, target, id, body) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: r.Changed, + Data: map[string]any{ + "stdout": r.Stdout, + "stderr": r.Stderr, + "exit_code": r.ExitCode, + }, + }, nil + }) +} + +// DockerInspect creates a task that inspects a Docker container on the +// target host. +func (p *Plan) DockerInspect( + name string, + target string, + id string, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.Inspect(ctx, target, id) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: false, + Data: map[string]any{ + "id": r.ID, + "name": r.Name, + "image": r.Image, + "state": r.State, + }, + }, nil + }) +} + +// DockerList creates a task that lists Docker containers on the target +// host. +func (p *Plan) DockerList( + name string, + target string, + params *gen.GetNodeContainerDockerParams, +) *Task { + return p.TaskFunc(name, func( + ctx context.Context, + c *osapiclient.Client, + ) (*Result, error) { + resp, err := c.Docker.List(ctx, target, params) + if err != nil { + return nil, err + } + + r := resp.Data.Results[0] + + return &Result{ + JobID: resp.Data.JobID, + Changed: false, + Data: map[string]any{ + "containers": r.Containers, + }, + }, nil + }) +} diff --git a/pkg/sdk/orchestrator/docker_public_test.go b/pkg/sdk/orchestrator/docker_public_test.go new file mode 100644 index 00000000..0bbda3e2 --- /dev/null +++ b/pkg/sdk/orchestrator/docker_public_test.go @@ -0,0 +1,711 @@ +package orchestrator_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/suite" + + osapiclient "github.com/retr0h/osapi/pkg/sdk/client" + "github.com/retr0h/osapi/pkg/sdk/client/gen" + "github.com/retr0h/osapi/pkg/sdk/orchestrator" +) + +type DockerPublicTestSuite struct { + suite.Suite +} + +func (s *DockerPublicTestSuite) TestDockerPull() { + tests := []struct { + name string + taskName string + target string + image string + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "pull-image", + target: "_any", + image: "ubuntu:24.04", + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("pull-image", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "pull-image", + target: "_any", + image: "alpine:latest", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000001","results":[{"hostname":"h1","image_id":"sha256:abc","tag":"latest","size":1024,"changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000001", result.JobID) + s.True(result.Changed) + s.Equal("sha256:abc", result.Data["image_id"]) + s.Equal("latest", result.Data["tag"]) + s.Equal(int64(1024), result.Data["size"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "pull-image", + target: "_any", + image: "alpine:latest", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerPull(tt.taskName, tt.target, tt.image) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerCreate() { + tests := []struct { + name string + taskName string + target string + body gen.DockerCreateRequest + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "create-container", + target: "_any", + body: gen.DockerCreateRequest{Image: "nginx:latest"}, + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("create-container", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "create-container", + target: "_any", + body: gen.DockerCreateRequest{Image: "nginx:latest"}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000002","results":[{"hostname":"h1","id":"c1","name":"web","image":"nginx:latest","state":"created","changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000002", result.JobID) + s.True(result.Changed) + s.Equal("c1", result.Data["id"]) + s.Equal("web", result.Data["name"]) + s.Equal("nginx:latest", result.Data["image"]) + s.Equal("created", result.Data["state"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "create-container", + target: "_any", + body: gen.DockerCreateRequest{Image: "nginx:latest"}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerCreate(tt.taskName, tt.target, tt.body) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerStart() { + tests := []struct { + name string + taskName string + target string + id string + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "start-container", + target: "_any", + id: "abc123", + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("start-container", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "start-container", + target: "_any", + id: "abc123", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000003","results":[{"hostname":"h1","id":"abc123","message":"started","changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000003", result.JobID) + s.True(result.Changed) + s.Equal("abc123", result.Data["id"]) + s.Equal("started", result.Data["message"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "start-container", + target: "_any", + id: "abc123", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerStart(tt.taskName, tt.target, tt.id) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerStop() { + tests := []struct { + name string + taskName string + target string + id string + body gen.DockerStopRequest + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "stop-container", + target: "_any", + id: "abc123", + body: gen.DockerStopRequest{}, + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("stop-container", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "stop-container", + target: "_any", + id: "abc123", + body: gen.DockerStopRequest{}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000004","results":[{"hostname":"h1","id":"abc123","message":"stopped","changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000004", result.JobID) + s.True(result.Changed) + s.Equal("abc123", result.Data["id"]) + s.Equal("stopped", result.Data["message"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "stop-container", + target: "_any", + id: "abc123", + body: gen.DockerStopRequest{}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerStop(tt.taskName, tt.target, tt.id, tt.body) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerRemove() { + tests := []struct { + name string + taskName string + target string + id string + params *gen.DeleteNodeContainerDockerByIDParams + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "remove-container", + target: "_any", + id: "abc123", + params: nil, + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("remove-container", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "remove-container", + target: "_any", + id: "abc123", + params: nil, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000005","results":[{"hostname":"h1","id":"abc123","message":"removed","changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000005", result.JobID) + s.True(result.Changed) + s.Equal("abc123", result.Data["id"]) + s.Equal("removed", result.Data["message"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "remove-container", + target: "_any", + id: "abc123", + params: nil, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerRemove(tt.taskName, tt.target, tt.id, tt.params) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerExec() { + tests := []struct { + name string + taskName string + target string + id string + body gen.DockerExecRequest + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "exec-cmd", + target: "_any", + id: "abc123", + body: gen.DockerExecRequest{Command: []string{"hostname"}}, + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("exec-cmd", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "exec-cmd", + target: "_any", + id: "abc123", + body: gen.DockerExecRequest{Command: []string{"hostname"}}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000006","results":[{"hostname":"h1","stdout":"web-01\n","stderr":"","exit_code":0,"changed":true}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000006", result.JobID) + s.True(result.Changed) + s.Equal("web-01\n", result.Data["stdout"]) + s.Equal("", result.Data["stderr"]) + s.Equal(0, result.Data["exit_code"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "exec-cmd", + target: "_any", + id: "abc123", + body: gen.DockerExecRequest{Command: []string{"hostname"}}, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerExec(tt.taskName, tt.target, tt.id, tt.body) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerInspect() { + tests := []struct { + name string + taskName string + target string + id string + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "inspect-container", + target: "_any", + id: "abc123", + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("inspect-container", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "inspect-container", + target: "_any", + id: "abc123", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000007","results":[{"hostname":"h1","id":"abc123","name":"web","image":"nginx:latest","state":"running"}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000007", result.JobID) + s.False(result.Changed) + s.Equal("abc123", result.Data["id"]) + s.Equal("web", result.Data["name"]) + s.Equal("nginx:latest", result.Data["image"]) + s.Equal("running", result.Data["state"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "inspect-container", + target: "_any", + id: "abc123", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerInspect(tt.taskName, tt.target, tt.id) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func (s *DockerPublicTestSuite) TestDockerList() { + tests := []struct { + name string + taskName string + target string + params *gen.GetNodeContainerDockerParams + handler http.HandlerFunc + validateFunc func(*orchestrator.Task, *osapiclient.Client) + }{ + { + name: "creates task with correct name", + taskName: "list-containers", + target: "_any", + params: nil, + validateFunc: func( + task *orchestrator.Task, + _ *osapiclient.Client, + ) { + s.NotNil(task) + s.Equal("list-containers", task.Name()) + }, + }, + { + name: "executes closure and returns result", + taskName: "list-containers", + target: "_any", + params: nil, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte( + `{"job_id":"00000000-0000-0000-0000-000000000008","results":[{"hostname":"h1","containers":[{"id":"c1","name":"web","image":"nginx","state":"running"}]}]}`, + )) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.NoError(err) + s.Equal("00000000-0000-0000-0000-000000000008", result.JobID) + s.False(result.Changed) + s.NotNil(result.Data["containers"]) + }, + }, + { + name: "returns error when SDK call fails", + taskName: "list-containers", + target: "_any", + params: nil, + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":"forbidden"}`)) + }, + validateFunc: func( + task *orchestrator.Task, + c *osapiclient.Client, + ) { + result, err := task.Fn()(context.Background(), c) + s.Error(err) + s.Nil(result) + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + var c *osapiclient.Client + if tt.handler != nil { + srv := httptest.NewServer(tt.handler) + defer srv.Close() + c = osapiclient.New(srv.URL, "token") + } + + plan := orchestrator.NewPlan(c) + task := plan.DockerList(tt.taskName, tt.target, tt.params) + tt.validateFunc(task, c) + s.Len(plan.Tasks(), 1) + }) + } +} + +func TestDockerPublicTestSuite(t *testing.T) { + suite.Run(t, new(DockerPublicTestSuite)) +} diff --git a/pkg/sdk/orchestrator/docker_target.go b/pkg/sdk/orchestrator/docker_target.go deleted file mode 100644 index ffd3c8d5..00000000 --- a/pkg/sdk/orchestrator/docker_target.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import ( - "context" - "fmt" - "runtime" - "sync" -) - -// ExecFn executes a command inside a container and returns stdout/stderr/exit code. -type ExecFn func( - ctx context.Context, - containerID string, - command []string, -) (stdout, stderr string, exitCode int, err error) - -// DockerTarget implements RuntimeTarget for Docker containers. -// It automatically deploys the osapi binary into the container -// on first use via Prepare. -type DockerTarget struct { - name string - image string - execFn ExecFn - binaryURL string - skipPrepare bool - - prepareOnce sync.Once - prepareErr error -} - -// NewDockerTarget creates a new Docker runtime target. -func NewDockerTarget( - name string, - image string, - execFn ExecFn, -) *DockerTarget { - return &DockerTarget{ - name: name, - image: image, - execFn: execFn, - } -} - -// Name returns the container name. -func (t *DockerTarget) Name() string { - return t.name -} - -// Runtime returns "docker". -func (t *DockerTarget) Runtime() string { - return "docker" -} - -// Image returns the container image. -func (t *DockerTarget) Image() string { - return t.image -} - -// SetBinaryURL overrides the GitHub release URL for the osapi binary. -func (t *DockerTarget) SetBinaryURL( - url string, -) { - t.binaryURL = url -} - -// SetSkipPrepare disables automatic binary deployment. -func (t *DockerTarget) SetSkipPrepare( - skip bool, -) { - t.skipPrepare = skip -} - -// Prepare ensures the osapi binary is deployed inside the container. -// It resolves the binary URL (from GitHub releases or a custom URL), -// downloads it inside the container, and makes it executable. -// Subsequent calls are no-ops. -func (t *DockerTarget) Prepare( - ctx context.Context, -) error { - if t.skipPrepare { - return nil - } - - t.prepareOnce.Do(func() { - t.prepareErr = t.doPrepare(ctx) - }) - - return t.prepareErr -} - -// doPrepare performs the actual binary deployment. -func (t *DockerTarget) doPrepare( - ctx context.Context, -) error { - url := t.binaryURL - if url == "" { - var err error - - url, err = resolveLatestBinaryURL(ctx, "linux", runtime.GOARCH) - if err != nil { - return fmt.Errorf("resolve osapi binary: %w", err) - } - } - - script := deployScript(url) - - _, stderr, exitCode, err := t.execFn( - ctx, - t.name, - []string{"sh", "-c", script}, - ) - if err != nil { - return fmt.Errorf("deploy osapi binary: %w", err) - } - - if exitCode != 0 { - return fmt.Errorf("deploy osapi binary (exit %d): %s", exitCode, stderr) - } - - return nil -} - -// ExecProvider runs a provider operation inside this container via -// docker exec. On first call it automatically deploys the osapi binary -// unless WithOSAPIBinarySkip was set. -func (t *DockerTarget) ExecProvider( - ctx context.Context, - provider string, - operation string, - data []byte, -) ([]byte, error) { - if err := t.Prepare(ctx); err != nil { - return nil, err - } - - cmd := []string{"/osapi", "provider", "run", provider, operation} - if len(data) > 0 { - cmd = append(cmd, "--data", string(data)) - } - - stdout, stderr, exitCode, err := t.execFn(ctx, t.name, cmd) - if err != nil { - return nil, fmt.Errorf("exec provider in container %s: %w", t.name, err) - } - - if exitCode != 0 { - return nil, fmt.Errorf( - "provider %s/%s failed (exit %d): %s", - provider, - operation, - exitCode, - stderr, - ) - } - - return []byte(stdout), nil -} diff --git a/pkg/sdk/orchestrator/docker_target_public_test.go b/pkg/sdk/orchestrator/docker_target_public_test.go deleted file mode 100644 index c211d14f..00000000 --- a/pkg/sdk/orchestrator/docker_target_public_test.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator_test - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/retr0h/osapi/pkg/sdk/orchestrator" -) - -// Compile-time interface check. -var _ orchestrator.RuntimeTarget = (*orchestrator.DockerTarget)(nil) - -type DockerTargetPublicTestSuite struct { - suite.Suite -} - -func TestDockerTargetPublicTestSuite(t *testing.T) { - suite.Run(t, new(DockerTargetPublicTestSuite)) -} - -func (s *DockerTargetPublicTestSuite) TestNewDockerTarget() { - tests := []struct { - name string - targetName string - image string - validateFunc func(target *orchestrator.DockerTarget) - }{ - { - name: "returns correct name and runtime", - targetName: "web", - image: "ubuntu:24.04", - validateFunc: func(target *orchestrator.DockerTarget) { - s.Equal("web", target.Name()) - s.Equal("docker", target.Runtime()) - s.Equal("ubuntu:24.04", target.Image()) - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - target := orchestrator.NewDockerTarget( - tt.targetName, - tt.image, - func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, nil - }, - ) - tt.validateFunc(target) - }) - } -} - -func (s *DockerTargetPublicTestSuite) TestExecProvider() { - tests := []struct { - name string - provider string - operation string - data []byte - execFn orchestrator.ExecFn - validateFunc func(result []byte, err error, capturedCmd []string) - }{ - { - name: "constructs correct command without data", - provider: "node.host", - operation: "get", - data: nil, - execFn: func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return `{"hostname":"web-01"}`, "", 0, nil - }, - validateFunc: func(result []byte, err error, _ []string) { - s.NoError(err) - s.Equal(`{"hostname":"web-01"}`, string(result)) - }, - }, - { - name: "constructs correct command with data", - provider: "network.dns", - operation: "set", - data: []byte(`{"servers":["8.8.8.8"]}`), - execFn: nil, // set below to capture command - validateFunc: func(_ []byte, _ error, capturedCmd []string) { - s.Equal([]string{ - "/osapi", "provider", "run", "network.dns", "set", - "--data", `{"servers":["8.8.8.8"]}`, - }, capturedCmd) - }, - }, - { - name: "returns error on non-zero exit", - provider: "node.host", - operation: "get", - data: nil, - execFn: func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "command not found", 1, nil - }, - validateFunc: func(result []byte, err error, _ []string) { - s.Error(err) - s.Nil(result) - s.Contains(err.Error(), "failed (exit 1)") - s.Contains(err.Error(), "command not found") - }, - }, - { - name: "returns error on exec failure", - provider: "node.host", - operation: "get", - data: nil, - execFn: func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, fmt.Errorf("connection refused") - }, - validateFunc: func(result []byte, err error, _ []string) { - s.Error(err) - s.Nil(result) - s.Contains(err.Error(), "exec provider in container") - s.Contains(err.Error(), "connection refused") - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - var capturedCmd []string - - execFn := tt.execFn - if execFn == nil { - execFn = func( - _ context.Context, - _ string, - cmd []string, - ) (string, string, int, error) { - capturedCmd = cmd - - return `{}`, "", 0, nil - } - } - - target := orchestrator.NewDockerTarget("web", "ubuntu:24.04", execFn) - target.SetSkipPrepare(true) - - result, err := target.ExecProvider( - context.Background(), - tt.provider, - tt.operation, - tt.data, - ) - tt.validateFunc(result, err, capturedCmd) - }) - } -} diff --git a/pkg/sdk/orchestrator/options.go b/pkg/sdk/orchestrator/options.go index 652d6190..694d9adf 100644 --- a/pkg/sdk/orchestrator/options.go +++ b/pkg/sdk/orchestrator/options.go @@ -95,11 +95,8 @@ type Hooks struct { // PlanConfig holds plan-level configuration. type PlanConfig struct { - OnErrorStrategy ErrorStrategy - Hooks *Hooks - DockerExecFn ExecFn - DockerBinaryURL string - DockerSkipDeploy bool + OnErrorStrategy ErrorStrategy + Hooks *Hooks } // PlanOption is a functional option for NewPlan. @@ -122,33 +119,3 @@ func WithHooks( cfg.Hooks = &hooks } } - -// WithDockerExecFn sets the exec function used by Plan.Docker() to -// execute commands inside Docker containers. -func WithDockerExecFn( - fn ExecFn, -) PlanOption { - return func(cfg *PlanConfig) { - cfg.DockerExecFn = fn - } -} - -// WithOSAPIBinaryURL overrides the default GitHub release URL for the -// osapi binary that gets deployed into Docker containers. Use this for -// custom builds, mirrors, or pre-release binaries. -func WithOSAPIBinaryURL( - url string, -) PlanOption { - return func(cfg *PlanConfig) { - cfg.DockerBinaryURL = url - } -} - -// WithOSAPIBinarySkip disables automatic binary deployment into Docker -// containers. Use this when the osapi binary is already baked into the -// container image. -func WithOSAPIBinarySkip() PlanOption { - return func(cfg *PlanConfig) { - cfg.DockerSkipDeploy = true - } -} diff --git a/pkg/sdk/orchestrator/options_public_test.go b/pkg/sdk/orchestrator/options_public_test.go index e6b01dd0..803428d8 100644 --- a/pkg/sdk/orchestrator/options_public_test.go +++ b/pkg/sdk/orchestrator/options_public_test.go @@ -142,43 +142,3 @@ func (s *OptionsPublicTestSuite) TestPlanOption() { }) } } - -func (s *OptionsPublicTestSuite) TestWithOSAPIBinaryURL() { - tests := []struct { - name string - url string - }{ - { - name: "sets binary URL", - url: "https://example.com/osapi", - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - cfg := &orchestrator.PlanConfig{} - opt := orchestrator.WithOSAPIBinaryURL(tt.url) - opt(cfg) - s.Equal(tt.url, cfg.DockerBinaryURL) - }) - } -} - -func (s *OptionsPublicTestSuite) TestWithOSAPIBinarySkip() { - tests := []struct { - name string - }{ - { - name: "sets skip deploy flag", - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - cfg := &orchestrator.PlanConfig{} - opt := orchestrator.WithOSAPIBinarySkip() - opt(cfg) - s.True(cfg.DockerSkipDeploy) - }) - } -} diff --git a/pkg/sdk/orchestrator/plan.go b/pkg/sdk/orchestrator/plan.go index 3a6d4215..aabcf21d 100644 --- a/pkg/sdk/orchestrator/plan.go +++ b/pkg/sdk/orchestrator/plan.go @@ -10,12 +10,9 @@ import ( // Plan is a DAG of tasks with dependency edges. type Plan struct { - client *osapiclient.Client - tasks []*Task - config PlanConfig - dockerExecFn ExecFn - dockerBinaryURL string - dockerSkipDeploy bool + client *osapiclient.Client + tasks []*Task + config PlanConfig } // NewPlan creates a new plan bound to an OSAPI client. @@ -32,11 +29,8 @@ func NewPlan( } return &Plan{ - client: client, - config: cfg, - dockerExecFn: cfg.DockerExecFn, - dockerBinaryURL: cfg.DockerBinaryURL, - dockerSkipDeploy: cfg.DockerSkipDeploy, + client: client, + config: cfg, } } diff --git a/pkg/sdk/orchestrator/plan_in.go b/pkg/sdk/orchestrator/plan_in.go deleted file mode 100644 index d31845c7..00000000 --- a/pkg/sdk/orchestrator/plan_in.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -// ScopedPlan routes provider operations through a RuntimeTarget. -type ScopedPlan struct { - plan *Plan - target RuntimeTarget -} - -// In creates a scoped plan context for the given runtime target. -func (p *Plan) In( - target RuntimeTarget, -) *ScopedPlan { - return &ScopedPlan{ - plan: p, - target: target, - } -} - -// Docker creates a DockerTarget bound to this plan. The target -// automatically deploys the osapi binary into the container on first -// provider call. Use WithOSAPIBinaryURL to override the download -// source, or WithOSAPIBinarySkip if the binary is pre-installed. -// Panics if no ExecFn was provided via WithDockerExecFn option. -func (p *Plan) Docker( - name string, - image string, -) *DockerTarget { - if p.dockerExecFn == nil { - panic("orchestrator: Plan.Docker() called without WithDockerExecFn option") - } - - t := NewDockerTarget(name, image, p.dockerExecFn) - t.binaryURL = p.dockerBinaryURL - t.skipPrepare = p.dockerSkipDeploy - - return t -} - -// Target returns the runtime target for this scoped plan. -func (sp *ScopedPlan) Target() RuntimeTarget { - return sp.target -} - -// TaskFunc creates a task on the parent plan within the target context. -func (sp *ScopedPlan) TaskFunc( - name string, - fn TaskFn, -) *Task { - return sp.plan.TaskFunc(name, fn) -} - -// TaskFuncWithResults creates a task with results within the target context. -func (sp *ScopedPlan) TaskFuncWithResults( - name string, - fn TaskFnWithResults, -) *Task { - return sp.plan.TaskFuncWithResults(name, fn) -} diff --git a/pkg/sdk/orchestrator/plan_in_public_test.go b/pkg/sdk/orchestrator/plan_in_public_test.go deleted file mode 100644 index ee943ae0..00000000 --- a/pkg/sdk/orchestrator/plan_in_public_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/suite" - - osapiclient "github.com/retr0h/osapi/pkg/sdk/client" - "github.com/retr0h/osapi/pkg/sdk/orchestrator" -) - -type PlanInPublicTestSuite struct { - suite.Suite -} - -func TestPlanInPublicTestSuite(t *testing.T) { - suite.Run(t, new(PlanInPublicTestSuite)) -} - -func (s *PlanInPublicTestSuite) TestDocker() { - tests := []struct { - name string - execFn orchestrator.ExecFn - targetName string - image string - validateFunc func(target *orchestrator.DockerTarget) - }{ - { - name: "returns target with correct name and image", - execFn: func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, nil - }, - targetName: "web-01", - image: "ubuntu:24.04", - validateFunc: func(target *orchestrator.DockerTarget) { - s.Equal("web-01", target.Name()) - s.Equal("ubuntu:24.04", target.Image()) - s.Equal("docker", target.Runtime()) - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - plan := orchestrator.NewPlan( - nil, - orchestrator.WithDockerExecFn(tt.execFn), - ) - - target := plan.Docker(tt.targetName, tt.image) - tt.validateFunc(target) - }) - } -} - -func (s *PlanInPublicTestSuite) TestDockerPanicsWithoutExecFn() { - plan := orchestrator.NewPlan(nil) - - s.Panics(func() { - plan.Docker("web", "ubuntu:24.04") - }) -} - -func (s *PlanInPublicTestSuite) TestIn() { - tests := []struct { - name string - validateFunc func(sp *orchestrator.ScopedPlan) - }{ - { - name: "returns scoped plan with target", - validateFunc: func(sp *orchestrator.ScopedPlan) { - s.NotNil(sp) - s.Equal("web", sp.Target().Name()) - s.Equal("docker", sp.Target().Runtime()) - }, - }, - } - - for _, tt := range tests { - s.Run(tt.name, func() { - execFn := func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, nil - } - - plan := orchestrator.NewPlan( - nil, - orchestrator.WithDockerExecFn(execFn), - ) - target := plan.Docker("web", "ubuntu:24.04") - sp := plan.In(target) - - tt.validateFunc(sp) - }) - } -} - -func (s *PlanInPublicTestSuite) TestScopedPlanTaskFunc() { - execFn := func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, nil - } - - plan := orchestrator.NewPlan( - nil, - orchestrator.WithDockerExecFn(execFn), - ) - target := plan.Docker("web", "ubuntu:24.04") - sp := plan.In(target) - - task := sp.TaskFunc("install-pkg", func( - _ context.Context, - _ *osapiclient.Client, - ) (*orchestrator.Result, error) { - return &orchestrator.Result{Changed: true}, nil - }) - - s.Equal("install-pkg", task.Name()) - s.Len(plan.Tasks(), 1) - s.Equal(task, plan.Tasks()[0]) -} - -func (s *PlanInPublicTestSuite) TestScopedPlanTaskFuncWithResults() { - execFn := func( - _ context.Context, - _ string, - _ []string, - ) (string, string, int, error) { - return "", "", 0, nil - } - - plan := orchestrator.NewPlan( - nil, - orchestrator.WithDockerExecFn(execFn), - ) - target := plan.Docker("web", "ubuntu:24.04") - sp := plan.In(target) - - task := sp.TaskFuncWithResults("check-status", func( - _ context.Context, - _ *osapiclient.Client, - _ orchestrator.Results, - ) (*orchestrator.Result, error) { - return &orchestrator.Result{Changed: false}, nil - }) - - s.Equal("check-status", task.Name()) - s.Len(plan.Tasks(), 1) - s.Equal(task, plan.Tasks()[0]) -} diff --git a/pkg/sdk/orchestrator/runtime_target.go b/pkg/sdk/orchestrator/runtime_target.go deleted file mode 100644 index 7a679006..00000000 --- a/pkg/sdk/orchestrator/runtime_target.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2026 John Dewey - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package orchestrator - -import "context" - -// RuntimeTarget represents a container runtime target that can execute -// provider operations. Implementations exist for Docker (now) and -// LXD/Podman (later). -type RuntimeTarget interface { - // Name returns the target name (container name). - Name() string - // Runtime returns the runtime type ("docker", "lxd", "podman"). - Runtime() string - // ExecProvider executes a provider operation inside the target. - ExecProvider(ctx context.Context, provider, operation string, data []byte) ([]byte, error) -} diff --git a/pkg/sdk/platform/platform.go b/pkg/sdk/platform/platform.go index 84e35689..fdd2f08f 100644 --- a/pkg/sdk/platform/platform.go +++ b/pkg/sdk/platform/platform.go @@ -19,8 +19,8 @@ // DEALINGS IN THE SOFTWARE. // Package platform provides cross-platform detection for OSAPI providers. -// Both the CLI (provider run) and the agent use this package to select -// the correct provider variant (ubuntu, darwin, or generic linux). +// The agent uses this package to select the correct provider variant +// (ubuntu, darwin, or generic linux). package platform import ( diff --git a/test/integration/container_test.go b/test/integration/docker_test.go similarity index 89% rename from test/integration/container_test.go rename to test/integration/docker_test.go index 67c600be..22ac203e 100644 --- a/test/integration/container_test.go +++ b/test/integration/docker_test.go @@ -28,12 +28,12 @@ import ( "github.com/stretchr/testify/suite" ) -type ContainerSmokeSuite struct { +type DockerSmokeSuite struct { suite.Suite } -func (s *ContainerSmokeSuite) TestContainerPull() { - skipWriteOp(s.T(), "CONTAINER_PULL") +func (s *DockerSmokeSuite) TestDockerPull() { + skipWriteOp(s.T(), "DOCKER_PULL") tests := []struct { name string @@ -43,7 +43,7 @@ func (s *ContainerSmokeSuite) TestContainerPull() { { name: "pulls alpine image and returns image id", args: []string{ - "client", "container", "pull", + "client", "container", "docker", "pull", "--image", "alpine:latest", "--json", }, @@ -79,12 +79,12 @@ func (s *ContainerSmokeSuite) TestContainerPull() { } } -func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { - skipWriteOp(s.T(), "CONTAINER_LIFECYCLE") +func (s *DockerSmokeSuite) TestDockerCreateListInspectStopRemove() { + skipWriteOp(s.T(), "DOCKER_LIFECYCLE") // Pull the image first so create does not fail. pullOut, _, pullCode := runCLI( - "client", "container", "pull", + "client", "container", "docker", "pull", "--image", "alpine:latest", "--json", ) @@ -96,7 +96,7 @@ func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { // Create createOut, _, createCode := runCLI( - "client", "container", "create", + "client", "container", "docker", "create", "--image", "alpine:latest", "--name", "integration-test-container", "--auto-start=false", @@ -119,7 +119,7 @@ func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { // List listOut, _, listCode := runCLI( - "client", "container", "list", + "client", "container", "docker", "list", "--state", "all", "--json", ) @@ -139,7 +139,7 @@ func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { // Inspect inspectOut, _, inspectCode := runCLI( - "client", "container", "inspect", + "client", "container", "docker", "inspect", "--id", "integration-test-container", "--json", ) @@ -161,7 +161,7 @@ func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { // Remove (force, in case the container is running) removeOut, _, removeCode := runCLI( - "client", "container", "remove", + "client", "container", "docker", "remove", "--id", "integration-test-container", "--force", "--json", @@ -182,7 +182,7 @@ func (s *ContainerSmokeSuite) TestContainerCreateListInspectStopRemove() { s.Contains(firstRemove, "changed") } -func (s *ContainerSmokeSuite) TestContainerList() { +func (s *DockerSmokeSuite) TestDockerList() { tests := []struct { name string args []string @@ -191,7 +191,7 @@ func (s *ContainerSmokeSuite) TestContainerList() { { name: "returns container list with results", args: []string{ - "client", "container", "list", + "client", "container", "docker", "list", "--state", "all", "--json", }, @@ -217,8 +217,8 @@ func (s *ContainerSmokeSuite) TestContainerList() { } } -func TestContainerSmokeSuite( +func TestDockerSmokeSuite( t *testing.T, ) { - suite.Run(t, new(ContainerSmokeSuite)) + suite.Run(t, new(DockerSmokeSuite)) }