From f0a22bcecadfc5d7211ac4abe672416722bb0125 Mon Sep 17 00:00:00 2001 From: konstantijn Date: Mon, 29 Sep 2025 15:27:21 +0200 Subject: [PATCH 1/2] Added support for commands and entrypoints in containers --- api/generated.go | 91 ++++++++++++++++++++++++++++++++++++ cmd/container.go | 24 ++++++++++ operations/Container.graphql | 2 + schema.graphql | 60 ++++++++++++++++++++++++ 4 files changed, 177 insertions(+) diff --git a/api/generated.go b/api/generated.go index 98d34e5..2cdf742 100644 --- a/api/generated.go +++ b/api/generated.go @@ -694,6 +694,29 @@ type ContainerCreateInput struct { // current health check unchanged you can omit this field. HealthCheck *HealthCheckInput `json:"healthCheck"` Type ContainerType `json:"type"` + // Entrypoint of the container. + // This field will overwrite the default entrypoint of the image. When the field is omitted, the default entrypoint of the image will be used. + // + // Null will reset the command to the default. + // + // Entry point is the first command executed when the container starts. It will receive the command as arguments. + // For example when the entrypoint is `python`, the command `app.py` will be executed as `python app.py`. + // + // This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + Entrypoint []string `json:"entrypoint"` + // Command to run. + // This is the command executed at the given schedule. + // When the field is omitted, the default command of the image will be used. + // + // Null will reset the command to the default. + // The command will be passed to the entrypoint as arguments. Use quotes to pass an argument with spaces. + // + // Environment variables can be used in the command by using the syntax `$(ENVIRONMENT_VARIABLE)`. + // + // Example: `echo "Hello $(NAME)"`. + // + // This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + Command []string `json:"command"` } // GetName returns ContainerCreateInput.Name, and is useful for accessing the field via an interface. @@ -734,6 +757,12 @@ func (v *ContainerCreateInput) GetHealthCheck() *HealthCheckInput { return v.Hea // GetType returns ContainerCreateInput.Type, and is useful for accessing the field via an interface. func (v *ContainerCreateInput) GetType() ContainerType { return v.Type } +// GetEntrypoint returns ContainerCreateInput.Entrypoint, and is useful for accessing the field via an interface. +func (v *ContainerCreateInput) GetEntrypoint() []string { return v.Entrypoint } + +// GetCommand returns ContainerCreateInput.Command, and is useful for accessing the field via an interface. +func (v *ContainerCreateInput) GetCommand() []string { return v.Command } + type ContainerJobCreateInput struct { Name string `json:"name"` Namespace string `json:"namespace"` @@ -983,6 +1012,29 @@ type ContainerModifyInput struct { // When you want to disable the health check you must send us a null value. To leave the // current health check unchanged you can omit this field. HealthCheck *HealthCheckInput `json:"healthCheck"` + // Entrypoint of the container. + // This field will overwrite the default entrypoint of the image. When the field is omitted, the default entrypoint of the image will be used. + // + // Null will reset the command to the default. + // + // Entry point is the first command executed when the container starts. It will receive the command as arguments. + // For example when the entrypoint is `python`, the command `app.py` will be executed as `python app.py`. + // + // This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + Entrypoint []string `json:"entrypoint"` + // Command to run. + // This is the command executed at the given schedule. + // When the field is omitted, the default command of the image will be used. + // + // Null will reset the command to the default. + // The command will be passed to the entrypoint as arguments. Use quotes to pass an argument with spaces. + // + // Environment variables can be used in the command by using the syntax `$(ENVIRONMENT_VARIABLE)`. + // + // Example: `echo "Hello $(NAME)"`. + // + // This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + Command []string `json:"command"` } // GetName returns ContainerModifyInput.Name, and is useful for accessing the field via an interface. @@ -1020,6 +1072,12 @@ func (v *ContainerModifyInput) GetScaling() *ScalingInput { return v.Scaling } // GetHealthCheck returns ContainerModifyInput.HealthCheck, and is useful for accessing the field via an interface. func (v *ContainerModifyInput) GetHealthCheck() *HealthCheckInput { return v.HealthCheck } +// GetEntrypoint returns ContainerModifyInput.Entrypoint, and is useful for accessing the field via an interface. +func (v *ContainerModifyInput) GetEntrypoint() []string { return v.Entrypoint } + +// GetCommand returns ContainerModifyInput.Command, and is useful for accessing the field via an interface. +func (v *ContainerModifyInput) GetCommand() []string { return v.Command } + // ContainerMounts includes the GraphQL fields of Mount requested by the fragment ContainerMounts. type ContainerMounts struct { Path string `json:"path"` @@ -1296,6 +1354,8 @@ type ContainerResult struct { Image string `json:"image"` PrivateRegistry *ContainerResultPrivateRegistry `json:"privateRegistry"` Resources ContainerResources `json:"resources"` + Command []string `json:"command"` + Entrypoint []string `json:"entrypoint"` EnvironmentVariables []EnvironmentVariableResult `json:"environmentVariables"` Ports []string `json:"ports"` Ingresses []ContainerResultIngressesIngress `json:"ingresses"` @@ -1323,6 +1383,12 @@ func (v *ContainerResult) GetPrivateRegistry() *ContainerResultPrivateRegistry { // GetResources returns ContainerResult.Resources, and is useful for accessing the field via an interface. func (v *ContainerResult) GetResources() ContainerResources { return v.Resources } +// GetCommand returns ContainerResult.Command, and is useful for accessing the field via an interface. +func (v *ContainerResult) GetCommand() []string { return v.Command } + +// GetEntrypoint returns ContainerResult.Entrypoint, and is useful for accessing the field via an interface. +func (v *ContainerResult) GetEntrypoint() []string { return v.Entrypoint } + // GetEnvironmentVariables returns ContainerResult.EnvironmentVariables, and is useful for accessing the field via an interface. func (v *ContainerResult) GetEnvironmentVariables() []EnvironmentVariableResult { return v.EnvironmentVariables @@ -2603,6 +2669,16 @@ func (v *containerListNamespaceContainersContainer) GetResources() ContainerReso return v.ContainerResult.Resources } +// GetCommand returns containerListNamespaceContainersContainer.Command, and is useful for accessing the field via an interface. +func (v *containerListNamespaceContainersContainer) GetCommand() []string { + return v.ContainerResult.Command +} + +// GetEntrypoint returns containerListNamespaceContainersContainer.Entrypoint, and is useful for accessing the field via an interface. +func (v *containerListNamespaceContainersContainer) GetEntrypoint() []string { + return v.ContainerResult.Entrypoint +} + // GetEnvironmentVariables returns containerListNamespaceContainersContainer.EnvironmentVariables, and is useful for accessing the field via an interface. func (v *containerListNamespaceContainersContainer) GetEnvironmentVariables() []EnvironmentVariableResult { return v.ContainerResult.EnvironmentVariables @@ -2688,6 +2764,10 @@ type __premarshalcontainerListNamespaceContainersContainer struct { Resources ContainerResources `json:"resources"` + Command []string `json:"command"` + + Entrypoint []string `json:"entrypoint"` + EnvironmentVariables []EnvironmentVariableResult `json:"environmentVariables"` Ports []string `json:"ports"` @@ -2726,6 +2806,8 @@ func (v *containerListNamespaceContainersContainer) __premarshalJSON() (*__prema retval.Image = v.ContainerResult.Image retval.PrivateRegistry = v.ContainerResult.PrivateRegistry retval.Resources = v.ContainerResult.Resources + retval.Command = v.ContainerResult.Command + retval.Entrypoint = v.ContainerResult.Entrypoint retval.EnvironmentVariables = v.ContainerResult.EnvironmentVariables retval.Ports = v.ContainerResult.Ports retval.Ingresses = v.ContainerResult.Ingresses @@ -4085,6 +4167,8 @@ fragment ContainerResult on Container { name } resources + command + entrypoint environmentVariables { ... EnvironmentVariableResult } @@ -4174,6 +4258,8 @@ fragment ContainerResult on Container { name } resources + command + entrypoint environmentVariables { ... EnvironmentVariableResult } @@ -4611,6 +4697,8 @@ fragment ContainerResult on Container { name } resources + command + entrypoint environmentVariables { ... EnvironmentVariableResult } @@ -4698,6 +4786,8 @@ fragment ContainerResult on Container { name } resources + command + entrypoint environmentVariables { ... EnvironmentVariableResult } @@ -4746,6 +4836,7 @@ fragment ContainerMounts on Mount { } ` +// @genqlien(omitempty: true) func containerModify( ctx_ context.Context, client_ graphql.Client, diff --git a/cmd/container.go b/cmd/container.go index 99416eb..461133a 100644 --- a/cmd/container.go +++ b/cmd/container.go @@ -78,6 +78,8 @@ var createContainerCmd = &cobra.Command{ resources, _ := cmd.Flags().GetString("resources") environmentVariables, _ := cmd.Flags().GetStringArray("env") secrets, _ := cmd.Flags().GetStringArray("secret") + command, _ := cmd.Flags().GetStringArray("command") + entrypoint, _ := cmd.Flags().GetStringArray("entrypoint") envs := append(envsToApi(environmentVariables, false, api.StatePresent), envsToApi(secrets, true, api.StatePresent)...) @@ -87,6 +89,8 @@ var createContainerCmd = &cobra.Command{ Resources: api.ContainerResources(resources), Image: image, EnvironmentVariables: envs, + Entrypoint: entrypoint, + Command: command, Mounts: []api.MountInput{}, Ports: []string{}, Ingresses: []api.IngressInput{}, @@ -115,6 +119,8 @@ var createStarterContainerCmd = &cobra.Command{ image, _ := cmd.Flags().GetString("image") environmentVariables, _ := cmd.Flags().GetStringArray("env") secrets, _ := cmd.Flags().GetStringArray("secret") + command, _ := cmd.Flags().GetStringArray("command") + entrypoint, _ := cmd.Flags().GetStringArray("entrypoint") envs := append(envsToApi(environmentVariables, false, api.StatePresent), envsToApi(secrets, true, api.StatePresent)...) @@ -124,6 +130,8 @@ var createStarterContainerCmd = &cobra.Command{ Resources: api.ContainerResourcesCpu250Ram500, Image: image, EnvironmentVariables: envs, + Entrypoint: entrypoint, + Command: command, Mounts: []api.MountInput{}, Ports: []string{}, Ingresses: []api.IngressInput{}, @@ -156,6 +164,8 @@ var modifyContainerCmd = &cobra.Command{ removedEnvironmentVariables, _ := cmd.Flags().GetStringArray("remove-env") registry, _ := cmd.Flags().GetString("registry") removeRegistry, _ := cmd.Flags().GetBool("remove-registry") + command, _ := cmd.Flags().GetStringArray("command") + entrypoint, _ := cmd.Flags().GetStringArray("entrypoint") client := api.NewClient() @@ -199,6 +209,14 @@ var modifyContainerCmd = &cobra.Command{ input.Resources = &resources } + if len(command) > 0 { + input.Command = command + } + + if len(entrypoint) > 0 { + input.Entrypoint = entrypoint + } + container, err := client.ContainerModify(input) if err != nil { log.Fatalf("Failed to modify container: %v", err) @@ -242,6 +260,8 @@ func init() { createContainerCmd.MarkFlagRequired("namespace") createContainerCmd.MarkFlagRequired("name") createContainerCmd.MarkFlagRequired("image") + createContainerCmd.Flags().StringArray("entrypoint", []string{}, "Entrypoint for the container") + createContainerCmd.Flags().StringArray("command", []string{}, "Command to run in the container") containerCmd.AddCommand(createContainerCmd) createStarterContainerCmd.Flags().String("namespace", "", "Namespace") @@ -249,6 +269,8 @@ func init() { createStarterContainerCmd.Flags().String("image", "", "Container image") createStarterContainerCmd.Flags().StringArray("env", []string{}, "Container environment variables") createStarterContainerCmd.Flags().StringArray("secret", []string{}, "Container secrets") + createStarterContainerCmd.Flags().StringArray("entrypoint", []string{}, "Entrypoint for the container") + createStarterContainerCmd.Flags().StringArray("command", []string{}, "Command to run in the container") createStarterContainerCmd.MarkFlagRequired("namespace") createStarterContainerCmd.MarkFlagRequired("name") createStarterContainerCmd.MarkFlagRequired("image") @@ -258,6 +280,8 @@ func init() { modifyContainerCmd.Flags().String("name", "", "Name for the container") modifyContainerCmd.Flags().String("image", "", "Container image") modifyContainerCmd.Flags().String("resources", "", "Container resources") + modifyContainerCmd.Flags().StringArray("command", []string{}, "Command to run in the container") + modifyContainerCmd.Flags().StringArray("entrypoint", []string{}, "Entrypoint for the container") modifyContainerCmd.Flags().StringArray("env", []string{}, "Container environment variables") modifyContainerCmd.Flags().StringArray("secret", []string{}, "Container secrets") modifyContainerCmd.Flags().StringArray("remove-env", []string{}, "Container remove environment variables") diff --git a/operations/Container.graphql b/operations/Container.graphql index 07f7b10..04a14f2 100644 --- a/operations/Container.graphql +++ b/operations/Container.graphql @@ -5,6 +5,8 @@ fragment ContainerResult on Container { name } resources + command + entrypoint # @genqlient(flatten: true) environmentVariables { ... EnvironmentVariableResult diff --git a/schema.graphql b/schema.graphql index b0562fb..061fb16 100644 --- a/schema.graphql +++ b/schema.graphql @@ -236,9 +236,11 @@ input ConfigureContainerInput { type Container { autoScaling: AutoScaling availableReplicas: Int! + command: [String!] containerType: ContainerType! @deprecated(reason: "use `type` instead") createdAt: DateTime! deletedAt: DateTime @deprecated(reason: "this field will be removed in the future, and doesn't have a replacement") + entrypoint: [String!] environmentVariables: [EnvironmentVariable!]! healthCheck: HealthCheck id: ID! @@ -302,6 +304,35 @@ input ContainerCreateInput { "" type: ContainerType! = DEFAULT + + """ + Entrypoint of the container. + This field will overwrite the default entrypoint of the image. When the field is omitted, the default entrypoint of the image will be used. + + Null will reset the command to the default. + + Entry point is the first command executed when the container starts. It will receive the command as arguments. + For example when the entrypoint is `python`, the command `app.py` will be executed as `python app.py`. + + This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + """ + entrypoint: [String!] + + """ + Command to run. + This is the command executed at the given schedule. + When the field is omitted, the default command of the image will be used. + + Null will reset the command to the default. + The command will be passed to the entrypoint as arguments. Use quotes to pass an argument with spaces. + + Environment variables can be used in the command by using the syntax `$(ENVIRONMENT_VARIABLE)`. + + Example: `echo "Hello $(NAME)"`. + + This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + """ + command: [String!] } input ContainerDeleteInput { @@ -510,6 +541,35 @@ input ContainerModifyInput { current health check unchanged you can omit this field. """ healthCheck: HealthCheckInput + + """ + Entrypoint of the container. + This field will overwrite the default entrypoint of the image. When the field is omitted, the default entrypoint of the image will be used. + + Null will reset the command to the default. + + Entry point is the first command executed when the container starts. It will receive the command as arguments. + For example when the entrypoint is `python`, the command `app.py` will be executed as `python app.py`. + + This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + """ + entrypoint: [String!] + + """ + Command to run. + This is the command executed at the given schedule. + When the field is omitted, the default command of the image will be used. + + Null will reset the command to the default. + The command will be passed to the entrypoint as arguments. Use quotes to pass an argument with spaces. + + Environment variables can be used in the command by using the syntax `$(ENVIRONMENT_VARIABLE)`. + + Example: `echo "Hello $(NAME)"`. + + This field is defined in docker exec format. https://docs.docker.com/reference/dockerfile/#shell-and-exec-form + """ + command: [String!] } input ContainerResourceInput { From 8f786737d83dfa6dbb62907c76da848cc0d67a99 Mon Sep 17 00:00:00 2001 From: Stijn Mommersteeg Date: Mon, 29 Sep 2025 17:00:02 +0200 Subject: [PATCH 2/2] Fixed container result --- api/container.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/container.go b/api/container.go index 9aa8029..1c2aabc 100644 --- a/api/container.go +++ b/api/container.go @@ -98,6 +98,8 @@ func toContainerResult(container ContainerResult) (ContainerResult, error) { AutoScaling: autoScaling, Locked: container.Locked, State: container.State, + Command: container.Command, + Entrypoint: container.Entrypoint, }, nil }