From a822ff9da99ff104e8a107ab61e6ecbf4022b961 Mon Sep 17 00:00:00 2001 From: Thorsten Klein Date: Tue, 21 Nov 2023 06:19:49 +0100 Subject: [PATCH] add: user directive in Acornfile (e.g. `user: "1000:1000"`) (#718) (#2334) --- .../api.acorn.io/v1/zz_generated.deepcopy.go | 5 + pkg/apis/internal.acorn.io/v1/appspec.go | 6 + pkg/apis/internal.acorn.io/v1/unmarshal.go | 44 ++++++ .../internal.acorn.io/v1/unmarshal_test.go | 147 ++++++++++++++++++ .../v1/zz_generated.deepcopy.go | 20 +++ pkg/appdefinition/acornfile-schema.acorn | 3 + pkg/appdefinition/appdefinition_test.go | 49 ++++++ pkg/controller/appdefinition/deploy.go | 7 + pkg/controller/appdefinition/deploy_test.go | 36 +++++ pkg/openapi/generated/openapi_generated.go | 46 +++++- 10 files changed, 360 insertions(+), 3 deletions(-) diff --git a/pkg/apis/api.acorn.io/v1/zz_generated.deepcopy.go b/pkg/apis/api.acorn.io/v1/zz_generated.deepcopy.go index 8d6c50517..c0b10b822 100644 --- a/pkg/apis/api.acorn.io/v1/zz_generated.deepcopy.go +++ b/pkg/apis/api.acorn.io/v1/zz_generated.deepcopy.go @@ -1032,6 +1032,11 @@ func (in *EmbeddedContainer) DeepCopyInto(out *EmbeddedContainer) { *out = new(int64) **out = **in } + if in.UserContext != nil { + in, out := &in.UserContext, &out.UserContext + *out = new(internal_acorn_iov1.UserContext) + **out = **in + } out.Metrics = in.Metrics if in.Scale != nil { in, out := &in.Scale, &out.Scale diff --git a/pkg/apis/internal.acorn.io/v1/appspec.go b/pkg/apis/internal.acorn.io/v1/appspec.go index 9d3c265b5..81b455495 100644 --- a/pkg/apis/internal.acorn.io/v1/appspec.go +++ b/pkg/apis/internal.acorn.io/v1/appspec.go @@ -238,6 +238,11 @@ type PolicyRule struct { Scopes []string `json:"scopes,omitempty"` } +type UserContext struct { + UID int64 `json:"uid,omitempty"` + GID int64 `json:"gid,omitempty"` +} + func slicePermutation(in []string) (result [][]string) { if len(in) == 0 { return [][]string{nil} @@ -640,6 +645,7 @@ type Container struct { Permissions *Permissions `json:"permissions,omitempty"` ComputeClass *string `json:"class,omitempty"` Memory *int64 `json:"memory,omitempty"` + UserContext *UserContext `json:"user,omitempty"` // Metrics is available on containers and jobs, but not sidecars Metrics MetricsDef `json:"metrics,omitempty"` diff --git a/pkg/apis/internal.acorn.io/v1/unmarshal.go b/pkg/apis/internal.acorn.io/v1/unmarshal.go index fe3c5fb33..6872c2986 100644 --- a/pkg/apis/internal.acorn.io/v1/unmarshal.go +++ b/pkg/apis/internal.acorn.io/v1/unmarshal.go @@ -32,6 +32,50 @@ type routeTarget struct { TargetServiceName string `json:"targetServiceName,omitempty"` } +func (in *UserContext) UnmarshalJSON(data []byte) error { + if !isString(data) { + var d int64 + if err := json.Unmarshal(data, &d); err == nil { + in.UID = d + in.GID = d + return nil + } + type userContext UserContext + return json.Unmarshal(data, (*userContext)(in)) + } + + s, err := parseString(data) + if err != nil { + return err + } + parts := strings.Split(s, ":") + + if len(parts) > 2 { + return fmt.Errorf("invalid user context has extra part: %s", s) + } + + uid, err := strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return fmt.Errorf("failed to parse uid %s in %s: %w", parts[0], s, err) + } + in.UID = uid + in.GID = uid + + if len(parts) == 2 { + gid, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return fmt.Errorf("failed to parse gid %s in %s: %w", parts[1], s, err) + } + in.GID = gid + } + + if in.UID < 0 || in.GID < 0 { + return fmt.Errorf("invalid user context uid or gid < 0: %s", s) + } + + return nil +} + func (in *routeTarget) UnmarshalJSON(data []byte) error { if !isString(data) { type routeTargetType routeTarget diff --git a/pkg/apis/internal.acorn.io/v1/unmarshal_test.go b/pkg/apis/internal.acorn.io/v1/unmarshal_test.go index 48d9ff78c..6d5046836 100644 --- a/pkg/apis/internal.acorn.io/v1/unmarshal_test.go +++ b/pkg/apis/internal.acorn.io/v1/unmarshal_test.go @@ -1,10 +1,15 @@ package v1 import ( + "encoding/json" + "fmt" "os" + "strconv" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestParseHostnameBinding(t *testing.T) { @@ -43,3 +48,145 @@ func TestParseEnv(t *testing.T) { Value: "y111", }, f[1]) } + +func TestUserContextUnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + expected *UserContext + wantErr bool + }{ + { + name: "valid uid+gid", + data: []byte(`"123:456"`), + expected: &UserContext{ + UID: 123, + GID: 456, + }, + wantErr: false, + }, + { + name: "valid uid only int", + data: []byte(`123`), + expected: &UserContext{ + UID: 123, + GID: 123, + }, + wantErr: false, + }, + { + name: "invalid uid+gid no quotes", + data: []byte(`123:456`), + expected: nil, + wantErr: true, + }, + { + name: "invalid uid", + data: []byte(`"abc:456"`), + expected: nil, + wantErr: true, + }, + { + name: "invalid gid", + data: []byte(`"123:def"`), + expected: nil, + wantErr: true, + }, + { + name: "valid uid only string", + data: []byte(`"123"`), + expected: &UserContext{ + UID: 123, + GID: 123, + }, + wantErr: false, + }, + { + name: "invalid extra field", + data: []byte(`"1000:1000:1000"`), + expected: nil, + wantErr: true, + }, + { + name: "invalid missing uid", + data: []byte(`":1000"`), + expected: nil, + wantErr: true, + }, + { + name: "invalid missing gid", + data: []byte(`"1000:"`), + expected: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var uc UserContext + err := uc.UnmarshalJSON(tt.data) + if tt.wantErr { + assert.Error(t, err) + assert.Nil(t, tt.expected) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, &uc) + } + }) + } +} + +func FuzzUserContextUnmarshalJSON(f *testing.F) { + // Add seed corpus + f.Add([]byte(`123`)) // Simple integer + f.Add([]byte(`"123:456"`)) // String with UID and GID + f.Add([]byte(`":123"`)) // String with UID missing + f.Add([]byte(`"123:"`)) // String with GID missing + f.Add([]byte(`"not a number"`)) // Invalid string + f.Add([]byte(`{"UID": 456, "GID": 789}`)) // JSON object + f.Add([]byte(`{"invalid": "json"}`)) // Invalid JSON for UserContext + f.Add([]byte(`["not", "object"]`)) // Invalid type + + f.Fuzz(func(t *testing.T, data []byte) { + var userContext UserContext + err := userContext.UnmarshalJSON(data) + + if isString(data) { + // If the data is string, check for valid UID/GID parsing or error + s, _ := parseString(data) + parts := strings.Split(s, ":") + if len(parts) == 1 || len(parts) == 2 { + _, errUID := strconv.ParseInt(parts[0], 10, 64) + if len(parts) == 2 { + _, errGID := strconv.ParseInt(parts[1], 10, 64) + if errUID != nil || errGID != nil { + require.Error(t, err, fmt.Errorf("Expected error for invalid UID/GID in string but got nil")) + } + } else if errUID != nil { + require.Error(t, err, fmt.Errorf("Expected error for invalid UID in string but got nil")) + } + } + } else { + // Check for valid int or JSON + var tempInt int64 + if errUnmarshal := json.Unmarshal(data, &tempInt); errUnmarshal == nil { + // If it's a valid integer, expect no error from UnmarshalJSON + require.NoError(t, err, fmt.Errorf("Expected no error for valid integer input but got %w", err)) + if userContext.UID != tempInt || userContext.GID != tempInt { + t.Errorf("Expected UID and GID to be %v, got UID: %v, GID: %v", tempInt, userContext.UID, userContext.GID) + } + } else { + // Check if it's valid JSON that can be unmarshalled + var temp interface{} + if json.Unmarshal(data, &temp) == nil { + _, ok := temp.(map[string]interface{}) + if !ok { + require.Error(t, err, fmt.Errorf("Expected error for non-object JSON but got nil")) + } + } else { + require.Error(t, err, fmt.Errorf("Expected error for invalid JSON but got nil")) + } + } + } + }) +} diff --git a/pkg/apis/internal.acorn.io/v1/zz_generated.deepcopy.go b/pkg/apis/internal.acorn.io/v1/zz_generated.deepcopy.go index ceaa203ca..4723a1893 100644 --- a/pkg/apis/internal.acorn.io/v1/zz_generated.deepcopy.go +++ b/pkg/apis/internal.acorn.io/v1/zz_generated.deepcopy.go @@ -1260,6 +1260,11 @@ func (in *Container) DeepCopyInto(out *Container) { *out = new(int64) **out = **in } + if in.UserContext != nil { + in, out := &in.UserContext, &out.UserContext + *out = new(UserContext) + **out = **in + } out.Metrics = in.Metrics if in.Scale != nil { in, out := &in.Scale, &out.Scale @@ -3521,6 +3526,21 @@ func (in *TCPProbe) DeepCopy() *TCPProbe { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserContext) DeepCopyInto(out *UserContext) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserContext. +func (in *UserContext) DeepCopy() *UserContext { + if in == nil { + return nil + } + out := new(UserContext) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VCS) DeepCopyInto(out *VCS) { *out = *in diff --git a/pkg/appdefinition/acornfile-schema.acorn b/pkg/appdefinition/acornfile-schema.acorn index a2613165a..3347dca69 100644 --- a/pkg/appdefinition/acornfile-schema.acorn +++ b/pkg/appdefinition/acornfile-schema.acorn @@ -215,12 +215,15 @@ let types: { match "probes|probe": Probes match "depends[oO]n|depends_on|consumes": string || [string] match "mem|memory": int + user?: User permissions?: { rules: [RuleSpec] clusterRules?: [ClusterRuleSpec] } } + User: (int > 0) || (string =~ "^[0-9]+(:[0-9]+)?$") + ShortVolumeRef: string =~ "^[a-z][-a-z0-9]*$" VolumeRef: string =~ "^volume://.+$" EphemeralRef: string =~ "^ephemeral://.*$|^$" diff --git a/pkg/appdefinition/appdefinition_test.go b/pkg/appdefinition/appdefinition_test.go index 64424b276..b4e193c01 100644 --- a/pkg/appdefinition/appdefinition_test.go +++ b/pkg/appdefinition/appdefinition_test.go @@ -3058,3 +3058,52 @@ func TestNestedScopedLabels(t *testing.T) { }}`)) assert.Error(t, err) } + +func TestUserContext(t *testing.T) { + appImage, err := NewAppDefinition([]byte(`containers: foo: { + image: "foo:latest" + user: "1000:2000" + }`)) + if err != nil { + t.Fatal(err) + } + + appSpec, err := appImage.AppSpec() + if err != nil { + errors.Print(os.Stderr, err, nil) + t.Fatal(err) + } + + require.Equal(t, &v1.UserContext{UID: 1000, GID: 2000}, appSpec.Containers["foo"].UserContext) + + // GID default to UID + nAppImage, err := NewAppDefinition([]byte(`containers: foo: { + image: "foo:latest" + user: "1000" + }`)) + if err != nil { + t.Fatal(err) + } + + nAppSpec, err := nAppImage.AppSpec() + if err != nil { + errors.Print(os.Stderr, err, nil) + t.Fatal(err) + } + + require.Equal(t, &v1.UserContext{UID: 1000, GID: 1000}, nAppSpec.Containers["foo"].UserContext) + + // Expect error because of non-integer values + _, err = NewAppDefinition([]byte(`containers: foo: { + image: "foo:latest" + user: "me:you" + }`)) + require.Error(t, err) + + // Expect error because of third entry + _, err = NewAppDefinition([]byte(`containers: foo: { + image: "foo:latest" + user: "1000:1000:1000" + }`)) + require.Error(t, err) +} diff --git a/pkg/controller/appdefinition/deploy.go b/pkg/controller/appdefinition/deploy.go index 2d45929e2..1be66e044 100644 --- a/pkg/controller/appdefinition/deploy.go +++ b/pkg/controller/appdefinition/deploy.go @@ -454,6 +454,13 @@ func toContainer(app *v1.AppInstance, tag name.Reference, containerName string, Resources: app.Status.Scheduling[containerName].Requirements, } + if container.UserContext != nil { + containerObject.SecurityContext = &corev1.SecurityContext{ + RunAsUser: z.Pointer(container.UserContext.UID), + RunAsGroup: z.Pointer(container.UserContext.GID), + } + } + if addWait { // If a container exposes a port, then add a pre-stop lifecycle hook that sleeps for 5 seconds. This should allow the // endpoints controller to remove the pods IP on termination and stop sending traffic to the container. diff --git a/pkg/controller/appdefinition/deploy_test.go b/pkg/controller/appdefinition/deploy_test.go index 5c4a219a6..54e82bc4d 100644 --- a/pkg/controller/appdefinition/deploy_test.go +++ b/pkg/controller/appdefinition/deploy_test.go @@ -13,6 +13,7 @@ import ( "github.com/acorn-io/runtime/pkg/secrets" "github.com/google/go-containerregistry/pkg/name" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -318,3 +319,38 @@ func TestFiles(t *testing.T) { assert.Equal(t, []byte("d"), configMap.Data[toHash("ZA==")]) assert.Equal(t, []byte("e"), configMap.Data[toHash("ZQ==")]) } + +func TestUserContext(t *testing.T) { + app := &v1.AppInstance{ + Status: v1.AppInstanceStatus{ + AppSpec: v1.AppSpec{ + Containers: map[string]v1.Container{ + "foo": { + Image: "foo:latest", + UserContext: &v1.UserContext{ + UID: 1000, + GID: 2000, + }, + Sidecars: map[string]v1.Container{ + "bar": { + Image: "bar:latest", + UserContext: &v1.UserContext{ + UID: 3000, + GID: 4000, + }, + }, + }, + }, + }, + }, + }, + } + + objs := ToDeploymentsTest(t, app, testTag, nil) + dep := objs[1].(*appsv1.Deployment) + + require.Equal(t, int64(1000), *dep.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser) + require.Equal(t, int64(2000), *dep.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup) + require.Equal(t, int64(3000), *dep.Spec.Template.Spec.Containers[1].SecurityContext.RunAsUser) + require.Equal(t, int64(4000), *dep.Spec.Template.Spec.Containers[1].SecurityContext.RunAsGroup) +} diff --git a/pkg/openapi/generated/openapi_generated.go b/pkg/openapi/generated/openapi_generated.go index 183af3cfe..1062c642d 100644 --- a/pkg/openapi/generated/openapi_generated.go +++ b/pkg/openapi/generated/openapi_generated.go @@ -213,6 +213,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.SignatureRules": schema_pkg_apis_internalacornio_v1_SignatureRules(ref), "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.SignedBy": schema_pkg_apis_internalacornio_v1_SignedBy(ref), "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.TCPProbe": schema_pkg_apis_internalacornio_v1_TCPProbe(ref), + "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext": schema_pkg_apis_internalacornio_v1_UserContext(ref), "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VCS": schema_pkg_apis_internalacornio_v1_VCS(ref), "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeBinding": schema_pkg_apis_internalacornio_v1_VolumeBinding(ref), "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeDefault": schema_pkg_apis_internalacornio_v1_VolumeDefault(ref), @@ -2880,6 +2881,11 @@ func schema_pkg_apis_apiacornio_v1_ContainerReplicaSpec(ref common.ReferenceCall Format: "int64", }, }, + "user": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext"), + }, + }, "metrics": { SchemaProps: spec.SchemaProps{ Description: "Metrics is available on containers and jobs, but not sidecars", @@ -2973,7 +2979,7 @@ func schema_pkg_apis_apiacornio_v1_ContainerReplicaSpec(ref common.ReferenceCall }, }, Dependencies: []string{ - "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, + "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, } } @@ -3492,6 +3498,11 @@ func schema_pkg_apis_apiacornio_v1_EmbeddedContainer(ref common.ReferenceCallbac Format: "int64", }, }, + "user": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext"), + }, + }, "metrics": { SchemaProps: spec.SchemaProps{ Description: "Metrics is available on containers and jobs, but not sidecars", @@ -3555,7 +3566,7 @@ func schema_pkg_apis_apiacornio_v1_EmbeddedContainer(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, + "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, } } @@ -8311,6 +8322,11 @@ func schema_pkg_apis_internalacornio_v1_Container(ref common.ReferenceCallback) Format: "int64", }, }, + "user": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext"), + }, + }, "metrics": { SchemaProps: spec.SchemaProps{ Description: "Metrics is available on containers and jobs, but not sidecars", @@ -8374,7 +8390,7 @@ func schema_pkg_apis_internalacornio_v1_Container(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, + "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Build", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Container", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Dependency", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.EnvVar", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.File", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.MetricsDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Permissions", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.PortDef", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.Probe", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.UserContext", "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1.VolumeMount"}, } } @@ -12538,6 +12554,30 @@ func schema_pkg_apis_internalacornio_v1_TCPProbe(ref common.ReferenceCallback) c } } +func schema_pkg_apis_internalacornio_v1_UserContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "gid": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_internalacornio_v1_VCS(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{