diff --git a/.github/workflows/builder_image_golang.yaml b/.github/workflows/builder_image_golang.yaml index 3f34ae0dd..6471e5c6a 100644 --- a/.github/workflows/builder_image_golang.yaml +++ b/.github/workflows/builder_image_golang.yaml @@ -12,7 +12,7 @@ permissions: env: GITHUB_REGISTRY: ghcr.io BUILDER_IMAGE_NAME: dyrector-io/dyrectorio/builder-images/golang - VERSION: 1 + VERSION: 2 jobs: build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/product_builder.yaml b/.github/workflows/product_builder.yaml index 486533d43..8993bd609 100644 --- a/.github/workflows/product_builder.yaml +++ b/.github/workflows/product_builder.yaml @@ -52,7 +52,7 @@ jobs: runs-on: ubuntu-22.04 container: # yamlfmt resides here because alpine doesn't provide yamlfmt package - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 steps: - name: Checkout uses: actions/checkout@v3 @@ -115,7 +115,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -139,7 +139,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -163,7 +163,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -189,7 +189,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -220,7 +220,7 @@ jobs: - go_integration - gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -615,7 +615,7 @@ jobs: packages: write runs-on: ubuntu-22.04 container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} diff --git a/.golangci.yml b/.golangci.yml index bbb3093f2..86b58953e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,17 +4,15 @@ run: - internal/cache linters-settings: depguard: - list-type: blacklist - packages: - # logging is allowed zerolog only - # is allowed to use only package - - log - packages-with-error-message: - - log: "logging is allowed only by github.com/rs/zerolog" + rules: + logblock: + deny: + - pkg: "log" + desc: "logging is allowed only by github.com/rs/zerolog" dupl: threshold: 100 forbidigo: - exclude_godoc_examples: false + exclude-godoc-examples: false forbid: - ^print.*$ - 'fmt\.Print.*' diff --git a/go.mod b/go.mod index 8b7dd95e2..eec335553 100644 --- a/go.mod +++ b/go.mod @@ -72,6 +72,7 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 github.com/imdario/mergo v0.3.15 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/go.sum b/go.sum index d39250afc..4bf89fa15 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -127,6 +128,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ilyakaznacheev/cleanenv v1.4.2 h1:nRqiriLMAC7tz7GzjzUTBHfzdzw6SQ7XvTagkFqe/zU= github.com/ilyakaznacheev/cleanenv v1.4.2/go.mod h1:i0owW+HDxeGKE0/JPREJOdSCPIyOnmh6C0xhWAkF/xA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= diff --git a/golang/Makefile b/golang/Makefile index 0afd847db..745b23ebd 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -16,10 +16,10 @@ LDFLAGS := -ldflags "-X '${PACKAGE}/internal/version.BuildTimestamp=${BUILD_TIME # default tag is latest for building image_version ?= latest -GOAIR=v1.42.0 -GOSEC=v2.15.0 -GOLANGCI=v1.51.2 -GOFUMPT=v0.4.0 +GOAIR=v1.45.0 +GOSEC=v2.17.0 +GOLANGCI=v1.54.2 +GOFUMPT=v0.5.0 YAMLFMT=v0.9.0 # support for: linux darwin windows diff --git a/golang/api/v1/deploy.go b/golang/api/v1/deploy.go index 551424601..17c0135b9 100644 --- a/golang/api/v1/deploy.go +++ b/golang/api/v1/deploy.go @@ -173,6 +173,8 @@ type ContainerConfig struct { IngressPath string `json:"ingressPath"` // ingress path for path based routing IngressStripPath bool `json:"ingressPathStrip"` + // ingress port to target + IngressPort uint16 `json:"ingressPort"` // for docker hosts, this is needs to be bytes: 1000000 ~1m IngressUploadLimit string `json:"ingressUploadLimit"` // if put together with another instances consume their shared configs eg. -common config map, generated from here @@ -208,7 +210,7 @@ type ContainerConfig struct { // k8s-only-section // Deployments strategy, on deployment how to restart underlying pods - // Values: Recreate (all-at-once), Rolling(one-by-one only if succeeds) + // Values: Recreate (all-at-once), RollingUpdate(one-by-one only if succeeds) DeploymentStrategy string `json:"deploymentStrategy"` // health check configuration HealthCheckConfig HealthCheckConfig `json:"healthCheck"` diff --git a/golang/cmd/crane/crane.go b/golang/cmd/crane/crane.go index b9681ce06..ecf21e854 100644 --- a/golang/cmd/crane/crane.go +++ b/golang/cmd/crane/crane.go @@ -70,7 +70,7 @@ func loadConfiguration() (*config.Configuration, *k8s.Secret) { return cfg, secretHandler } -func serve(cCtx *cli.Context) error { +func serve(_ *cli.Context) error { cfg, secretHandler := loadConfiguration() crane.Serve(cfg, secretHandler) @@ -98,7 +98,7 @@ func initKey(cCtx *cli.Context) error { return nil } -func getHealth(cCtx *cli.Context) error { +func getHealth(_ *cli.Context) error { healthy, err := health.GetHealthy() if err != nil { log.Error().Err(err).Send() diff --git a/golang/cmd/dagent/.env.example b/golang/cmd/dagent/.env.example index 82e8f745f..cdaed7b4b 100644 --- a/golang/cmd/dagent/.env.example +++ b/golang/cmd/dagent/.env.example @@ -23,7 +23,6 @@ DEFAULT_TAG=latest DEFAULT_TIMEOUT=5s GRPC_KEEPALIVE=60s HOST_DOCKER_SOCK_PATH=/var/run/docker.sock -HOST_MOUNT_PATH=/srv/dagent INTERNAL_MOUNT_PATH=/srv/dagent LOG_DEFAULT_SKIP=0 LOG_DEFAULT_TAKE=100 diff --git a/golang/cmd/dagent/dagent.go b/golang/cmd/dagent/dagent.go index 978cafbf2..fd7bdc32c 100644 --- a/golang/cmd/dagent/dagent.go +++ b/golang/cmd/dagent/dagent.go @@ -14,7 +14,7 @@ import ( cli "github.com/urfave/cli/v2" ) -func serve(cCtx *cli.Context) error { +func serve(_ *cli.Context) error { cfg := config.Configuration{} err := util.ReadConfig(&cfg) @@ -39,7 +39,7 @@ func serve(cCtx *cli.Context) error { return nil } -func getHealth(cCtx *cli.Context) error { +func getHealth(_ *cli.Context) error { healthy, err := health.GetHealthy() if err != nil { log.Error().Err(err).Send() diff --git a/golang/internal/grpc/grpc.go b/golang/internal/grpc/grpc.go index dfd89dab5..be8e3a9eb 100644 --- a/golang/internal/grpc/grpc.go +++ b/golang/internal/grpc/grpc.go @@ -64,8 +64,11 @@ type ContainerWatchContext struct { Error chan error } +// UpdateOptions options to increase update testability type UpdateOptions struct { - UpdateAlways bool + // always execute the update, regardless of the actual version + UpdateAlways bool + // if false will not restart by itself, only a message printed about the token events UseContainers bool } @@ -312,10 +315,9 @@ func (cl *ClientLoop) grpcLoop(connParams *ConnectionParams) { time.Sleep(time.Second) grpcConn.Client = nil continue - } else { - log.Info().Msg("Stream connection is up") - health.SetHealthGRPCStatus(true) } + log.Info().Msg("Stream connection is up") + health.SetHealthGRPCStatus(true) } command := new(agent.AgentCommand) diff --git a/golang/internal/mapper/grpc.go b/golang/internal/mapper/grpc.go index 8620de0de..f220d441d 100644 --- a/golang/internal/mapper/grpc.go +++ b/golang/internal/mapper/grpc.go @@ -20,6 +20,7 @@ import ( dockerTypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" + "github.com/iancoleman/strcase" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) @@ -126,6 +127,7 @@ func mapContainerConfig(in *agent.DeployRequest) v1.ContainerConfig { containerConfig.IngressUploadLimit = pointer.GetString(cc.Routing.UploadLimit) containerConfig.IngressPath = pointer.GetString(cc.Routing.Path) containerConfig.IngressStripPath = pointer.GetBool(cc.Routing.StripPath) + containerConfig.IngressPort = uint16(pointer.GetUint32(cc.Routing.Port)) } if cc.ConfigContainer != nil { @@ -169,7 +171,7 @@ func mapDagentConfig(dagent *agent.DagentContainerConfig, containerConfig *v1.Co } func mapCraneConfig(crane *agent.CraneContainerConfig, containerConfig *v1.ContainerConfig) { - containerConfig.DeploymentStrategy = crane.DeploymentStatregy.String() + containerConfig.DeploymentStrategy = strcase.ToCamel(crane.DeploymentStrategy.String()) if crane.ProxyHeaders != nil { containerConfig.ProxyHeaders = *crane.ProxyHeaders @@ -390,6 +392,9 @@ func mapVolumeLinks(in []*agent.VolumeLink) []v1.VolumeLink { } func MapContainerState(it *dockerTypes.Container, prefix string) *common.ContainerStateItem { + if it == nil { + return nil + } name := "" if len(it.Names) > 0 { name = strings.TrimPrefix(it.Names[0], "/") diff --git a/golang/internal/mapper/grpc_test.go b/golang/internal/mapper/grpc_test.go index 57e5d0571..2c21c5f9f 100644 --- a/golang/internal/mapper/grpc_test.go +++ b/golang/internal/mapper/grpc_test.go @@ -1,7 +1,7 @@ //go:build unit // +build unit -package mapper_test +package mapper import ( "testing" @@ -11,7 +11,6 @@ import ( "github.com/docker/docker/api/types/container" "github.com/dyrector-io/dyrectorio/golang/internal/config" "github.com/dyrector-io/dyrectorio/golang/internal/helper/image" - "github.com/dyrector-io/dyrectorio/golang/internal/mapper" builder "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" "github.com/dyrector-io/dyrectorio/protobuf/go/agent" "github.com/dyrector-io/dyrectorio/protobuf/go/common" @@ -25,7 +24,7 @@ func TestMapDeployImageRequest(t *testing.T) { req := testDeployRequest() cfg := testAppConfig() - res := mapper.MapDeployImage(req, cfg) + res := MapDeployImage(req, cfg) expected := testExpectedCommon(req) assert.Equal(t, expected, res) @@ -64,6 +63,7 @@ func testExpectedCommon(req *agent.DeployRequest) *v1.DeployImageRequest { ExposeTLS: true, IngressHost: "test-domain", IngressName: "", + IngressPort: 12345, IngressUploadLimit: "5Mi", Shared: false, ConfigContainer: &v1.ConfigContainer{ @@ -106,7 +106,7 @@ func testExpectedCommon(req *agent.DeployRequest) *v1.DeployImageRequest { Service: map[string]string{"annot2": "value2"}, Ingress: map[string]string{"annot3": "value3"}, }, - DeploymentStrategy: "RECREATE", + DeploymentStrategy: "Recreate", Labels: v1.Markers{ Deployment: map[string]string{"label1": "value1"}, Service: map[string]string{"label2": "value2"}, @@ -157,14 +157,14 @@ func TestMapPorts(t *testing.T) { }, } - bindings := mapper.MapPorts(ps) + bindings := MapPorts(ps) assert.Equal(t, expected, bindings) } func TestMapSecrets(t *testing.T) { kvl := testKeyValueList() - m := mapper.MapSecrets(kvl) + m := MapSecrets(kvl) expected := map[string]string{ "testKey-1": "testID-1", "testKey-2": "testID-2", @@ -174,13 +174,13 @@ func TestMapSecrets(t *testing.T) { } func TestMapDockerContainerEventToContainerState(t *testing.T) { - assert.Equal(t, common.ContainerState_WAITING, mapper.MapDockerContainerEventToContainerState("create")) - assert.Equal(t, common.ContainerState_REMOVED, mapper.MapDockerContainerEventToContainerState("destroy")) - assert.Equal(t, common.ContainerState_WAITING, mapper.MapDockerContainerEventToContainerState("pause")) - assert.Equal(t, common.ContainerState_RUNNING, mapper.MapDockerContainerEventToContainerState("restart")) - assert.Equal(t, common.ContainerState_RUNNING, mapper.MapDockerContainerEventToContainerState("start")) - assert.Equal(t, common.ContainerState_EXITED, mapper.MapDockerContainerEventToContainerState("stop")) - assert.Equal(t, common.ContainerState_EXITED, mapper.MapDockerContainerEventToContainerState("die")) + assert.Equal(t, common.ContainerState_WAITING, MapDockerContainerEventToContainerState("create")) + assert.Equal(t, common.ContainerState_REMOVED, MapDockerContainerEventToContainerState("destroy")) + assert.Equal(t, common.ContainerState_WAITING, MapDockerContainerEventToContainerState("pause")) + assert.Equal(t, common.ContainerState_RUNNING, MapDockerContainerEventToContainerState("restart")) + assert.Equal(t, common.ContainerState_RUNNING, MapDockerContainerEventToContainerState("start")) + assert.Equal(t, common.ContainerState_EXITED, MapDockerContainerEventToContainerState("stop")) + assert.Equal(t, common.ContainerState_EXITED, MapDockerContainerEventToContainerState("die")) } func testDeployRequest() *agent.DeployRequest { @@ -227,6 +227,7 @@ func testDeployRequest() *agent.DeployRequest { Routing: &common.Routing{ Domain: pointer.ToString("test-domain"), UploadLimit: &upLimit, + Port: pointer.ToUint32(12345), }, ConfigContainer: &common.ConfigContainer{ Image: "test-image", @@ -354,7 +355,7 @@ func testCraneConfig() *agent.CraneContainerConfig { ReadinessProbe: &rProbe, StartupProbe: &sProbe, }, - DeploymentStatregy: common.DeploymentStrategy_RECREATE.Enum(), + DeploymentStrategy: common.DeploymentStrategy_RECREATE.Enum(), } } @@ -480,10 +481,38 @@ func TestMapDeployImageLogConfig(t *testing.T) { req := testDeployRequestWithLogDriver(tC.driver) cfg := testAppConfig() - res := mapper.MapDeployImage(req, cfg) + res := MapDeployImage(req, cfg) expected := testExpectedCommonWithLogConfigType(req, tC.want) assert.Equal(t, expected, res) }) } } + +func TestCraneDeploymentStrategyMapping(t *testing.T) { + craneConfig := testCraneConfig() + testCases := []struct { + strat *common.DeploymentStrategy + exp string + desc string + }{ + { + desc: "expected Recreate", + exp: "Recreate", + strat: common.DeploymentStrategy_RECREATE.Enum(), + }, + { + desc: "expected RollingUpdate", + exp: "RollingUpdate", + strat: common.DeploymentStrategy_ROLLING_UPDATE.Enum(), + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + craneConfig.DeploymentStrategy = tC.strat + resultConfig := v1.ContainerConfig{} + mapCraneConfig(craneConfig, &resultConfig) + assert.Equalf(t, tC.exp, resultConfig.DeploymentStrategy, "%s got: %s", tC.desc, resultConfig.DeploymentStrategy) + }) + } +} diff --git a/golang/internal/util/array.go b/golang/internal/util/array.go index aabdc7420..3f1ea8a5e 100644 --- a/golang/internal/util/array.go +++ b/golang/internal/util/array.go @@ -1,9 +1,22 @@ package util +func Contains[T comparable](arr []T, item T) bool { + for _, v := range arr { + if v == item { + return true + } + } + + return false +} + // simple contain check, not part of the stdlib unfortunately -func Contains[T comparable](arr []T, str T) bool { +func ContainsMatcher[T comparable](arr []T, item T, comp func(T, T) bool) bool { + if comp == nil { + return false + } for _, v := range arr { - if v == str { + if comp != nil && comp(v, item) { return true } } diff --git a/golang/internal/util/array_test.go b/golang/internal/util/array_test.go index f5cefb3a7..d5be88478 100644 --- a/golang/internal/util/array_test.go +++ b/golang/internal/util/array_test.go @@ -8,9 +8,21 @@ import ( "github.com/stretchr/testify/assert" ) -func TestContaints(t *testing.T) { +func TestContains(t *testing.T) { arr := []string{"as", "bs", "cs"} assert.True(t, util.Contains(arr, "bs"), "bs is part of the array, so it must be true") assert.False(t, util.Contains(arr, "xs"), "xs is not part of the array, so it must be false") } + +func TestContainsMatcher(t *testing.T) { + arr := []int{1, 2, 3, 4, 5, 6} + + intCompareFn := func(i1, i2 int) bool { + return i1 == i2 + } + + assert.True(t, util.ContainsMatcher(arr, 6, intCompareFn)) + assert.False(t, util.ContainsMatcher(arr, 9, intCompareFn)) + assert.False(t, util.ContainsMatcher(arr, 9, nil)) +} diff --git a/golang/pkg/builder/container/docker_container.go b/golang/pkg/builder/container/docker_container.go index a25f6c69e..02bb10800 100644 --- a/golang/pkg/builder/container/docker_container.go +++ b/golang/pkg/builder/container/docker_container.go @@ -40,6 +40,7 @@ func NewDockerContainer(cont *types.Container, preStartHooks, preStartHooks: preStartHooks, postStartHooks: postStartHooks, mountList: mountList, + envList: envList, } } @@ -72,10 +73,8 @@ func (d DockerContainer) Start(ctx context.Context, cli client.APIClient) error if err != nil { return err } - if hookError := execStartHooks(ctx, cli, d.GetName(), d.container, d.mountList, d.envList, d.postStartHooks); hookError != nil { - return hookError - } - return nil + + return execStartHooks(ctx, cli, d.GetName(), d.container, d.mountList, d.envList, d.postStartHooks) } func (d DockerContainer) StartWaitUntilExit(ctx context.Context, cli client.APIClient) (*WaitResult, error) { diff --git a/golang/pkg/crane/crane.go b/golang/pkg/crane/crane.go index 3db996d7f..63aa1d876 100644 --- a/golang/pkg/crane/crane.go +++ b/golang/pkg/crane/crane.go @@ -50,7 +50,7 @@ func Serve(cfg *config.Configuration, secretStore commonConfig.SecretStore) { }, secretStore) } -func grpcClose(ctx context.Context, reason agent.CloseReason, updateOptions grpc.UpdateOptions) error { +func grpcClose(ctx context.Context, reason agent.CloseReason, _ grpc.UpdateOptions) error { cfg := grpc.GetConfigFromContext(ctx).(*config.Configuration) if reason == agent.CloseReason_SHUTDOWN { log.Info().Msg("Remote shutdown requested") @@ -69,9 +69,8 @@ func grpcClose(ctx context.Context, reason agent.CloseReason, updateOptions grpc } } os.Exit(0) - } else { - log.Error().Int32("reason", int32(reason)).Msg("Close reason not implemented") } + log.Error().Int32("reason", int32(reason)).Msg("Close reason not implemented") return nil } diff --git a/golang/pkg/crane/k8s/deploy_facade.go b/golang/pkg/crane/k8s/deploy_facade.go index 463ee8515..bc6ae28fe 100644 --- a/golang/pkg/crane/k8s/deploy_facade.go +++ b/golang/pkg/crane/k8s/deploy_facade.go @@ -152,14 +152,10 @@ func (d *DeployFacade) PreDeploy() error { return err } - if err := d.secret.applySecrets( + return d.secret.applySecrets( d.namespace.name, d.params.ContainerConfig.Container, - d.params.ContainerConfig.Secrets); err != nil { - return err - } - - return nil + d.params.ContainerConfig.Secrets) } func (d *DeployFacade) Deploy() error { @@ -226,7 +222,9 @@ func (d *DeployFacade) Deploy() error { ingressPath: d.params.ContainerConfig.IngressPath, stripPrefix: d.params.ContainerConfig.IngressStripPath, uploadLimit: d.params.ContainerConfig.IngressUploadLimit, - ports: d.service.portsBound, + customHeaders: d.params.ContainerConfig.CustomHeaders, + port: d.params.ContainerConfig.IngressPort, + portList: d.service.portsBound, tls: d.params.ContainerConfig.ExposeTLS, proxyHeaders: d.params.ContainerConfig.ProxyHeaders, annotations: d.params.ContainerConfig.Annotations.Ingress, @@ -265,7 +263,7 @@ func (d *DeployFacade) Clear() error { } func Deploy(c context.Context, dog *dogger.DeploymentLogger, deployImageRequest *v1.DeployImageRequest, - versionData *v1.VersionData, + _ *v1.VersionData, ) error { cfg := grpc.GetConfigFromContext(c).(*config.Configuration) dog.Write(deployImageRequest.Strings(&cfg.CommonConfiguration)...) @@ -307,8 +305,5 @@ func Deploy(c context.Context, dog *dogger.DeploymentLogger, deployImageRequest return err } - if err := deployFacade.PostDeploy(); err != nil { - return err - } - return nil + return deployFacade.PostDeploy() } diff --git a/golang/pkg/crane/k8s/deployment.go b/golang/pkg/crane/k8s/deployment.go index 5b7a9c941..f95bcc0ca 100644 --- a/golang/pkg/crane/k8s/deployment.go +++ b/golang/pkg/crane/k8s/deployment.go @@ -98,6 +98,7 @@ func (d *Deployment) DeployDeployment(p *deploymentParams) error { WithSpec( appsv1.DeploymentSpec(). WithReplicas(1). + WithStrategy(appsv1.DeploymentStrategy().WithType(kappsv1.DeploymentStrategyType(p.containerConfig.DeploymentStrategy))). WithSelector(metav1.LabelSelector().WithMatchLabels(map[string]string{ "app": name, })). diff --git a/golang/pkg/crane/k8s/ingress.go b/golang/pkg/crane/k8s/ingress.go index d7fe5a2f0..203c367a2 100644 --- a/golang/pkg/crane/k8s/ingress.go +++ b/golang/pkg/crane/k8s/ingress.go @@ -31,7 +31,8 @@ type ingress struct { type DeployIngressOptions struct { namespace, containerName, ingressName, ingressHost, ingressPath, uploadLimit string stripPrefix bool - ports []int32 + port uint16 + portList []int32 tls, proxyHeaders bool customHeaders []string labels map[string]string @@ -52,10 +53,15 @@ func (ing *ingress) deployIngress(options *DeployIngressOptions) error { log.Error().Err(err).Stack().Msg("Error with ingress client") } - if len(options.ports) == 0 { + if options.port == 0 && len(options.portList) == 0 { return errors.New("empty ports, nothing to expose") } + routedPort := options.port + if routedPort == 0 { + routedPort = uint16(options.portList[0]) + } + ingressDomain := domain.GetHostRule( &domain.HostRouting{ Subdomain: options.ingressName, @@ -89,7 +95,7 @@ func (ing *ingress) deployIngress(options *DeployIngressOptions) error { netv1.IngressBackend().WithService( netv1.IngressServiceBackend(). WithName(options.containerName). - WithPort(netv1.ServiceBackendPort().WithNumber(options.ports[0])), + WithPort(netv1.ServiceBackendPort().WithNumber(int32(routedPort))), ), ), ))) diff --git a/golang/pkg/crane/k8s/service.go b/golang/pkg/crane/k8s/service.go index 92d7d9b80..aa3e69611 100644 --- a/golang/pkg/crane/k8s/service.go +++ b/golang/pkg/crane/k8s/service.go @@ -125,18 +125,11 @@ func getServicePorts(portBindings []builder.PortBinding, portRanges []builder.Po ports := []*acorev1.ServicePortApplyConfiguration{} for i := range portBindings { - var portNum int32 - if portBindings[i].PortBinding != nil { - portNum = int32(*portBindings[i].PortBinding) - } else { - portNum = int32(portBindings[i].ExposedPort) - } ports = append(ports, acorev1.ServicePort(). - WithName(fmt.Sprintf("tcp-%v", portNum)). + WithName(fmt.Sprintf("tcp-%v", portBindings[i].ExposedPort)). WithProtocol(corev1.ProtocolTCP). - WithPort(portNum). - WithTargetPort(intstr.FromInt(int(portBindings[i].ExposedPort)))) + WithPort(int32(portBindings[i].ExposedPort))) } for i := range portRanges { diff --git a/golang/pkg/dagent/README.md b/golang/pkg/dagent/README.md index 83e43569f..ec37d0426 100644 --- a/golang/pkg/dagent/README.md +++ b/golang/pkg/dagent/README.md @@ -92,7 +92,6 @@ Configuration will take place before starting up the application, and store the | DATA_MOUNT_PATH | This should match the mount path that is the root of configurations and containers | /srv/dagent | | DEFAULT_TAG | default tag to use with container images in deployment | latest | | HOST_DOCKER_SOCK_PATH | Path of `docker.sock` or other local/remote address where we can communicate with docker | /var/run/docker.sock | -| HOST_MOUNT_PATH | Host mount path default | /srv/dagent | | INTERNAL_MOUNT_PATH | Containers mount path default | /srv/dagent | | LOG_DEFAULT_SKIP | Loglines to skip | 0 | | LOG_DEFAULT_TAKE | Loglines to take | 100 | diff --git a/golang/pkg/dagent/caps/parser.go b/golang/pkg/dagent/caps/parser.go index ca16d1462..ad52d214c 100644 --- a/golang/pkg/dagent/caps/parser.go +++ b/golang/pkg/dagent/caps/parser.go @@ -23,26 +23,25 @@ func ParseLabelsIntoContainerConfig(labels map[string]string, config *v1.Contain for key, value := range labels { if key != "io.dyrector.cap.network.v1" { continue - } else { - network := NetworkLabel{} - - err := json.Unmarshal([]byte(value), &network) - if err != nil { - log.Error().Stack().Err(err).Send() - } - - ports := []builder.PortBinding{} - if config.Ports != nil && len(config.Ports) > 0 { - ports = config.Ports - } - for i := range network.Ports { - ports = append(ports, builder.PortBinding{ - ExposedPort: uint16(network.Ports[i].Listening), - PortBinding: pointer.ToUint16(0), - }) - } - - config.Ports = ports } + network := NetworkLabel{} + + err := json.Unmarshal([]byte(value), &network) + if err != nil { + log.Error().Stack().Err(err).Send() + } + + ports := []builder.PortBinding{} + if config.Ports != nil && len(config.Ports) > 0 { + ports = config.Ports + } + for i := range network.Ports { + ports = append(ports, builder.PortBinding{ + ExposedPort: uint16(network.Ports[i].Listening), + PortBinding: pointer.ToUint16(0), + }) + } + + config.Ports = ports } } diff --git a/golang/pkg/dagent/dagent.go b/golang/pkg/dagent/dagent.go index c4fa5aa3d..a71e16419 100644 --- a/golang/pkg/dagent/dagent.go +++ b/golang/pkg/dagent/dagent.go @@ -14,7 +14,7 @@ import ( ) func Serve(cfg *config.Configuration) { - utils.PreflightChecks(cfg) + utils.PreflightChecks() log.Info().Msg("Starting dyrector.io DAgent service") if cfg.TraefikEnabled { diff --git a/golang/pkg/dagent/utils/preflight.go b/golang/pkg/dagent/utils/preflight.go index 4cbeebd6c..70e5f2576 100644 --- a/golang/pkg/dagent/utils/preflight.go +++ b/golang/pkg/dagent/utils/preflight.go @@ -9,10 +9,9 @@ import ( dockerHelper "github.com/dyrector-io/dyrectorio/golang/internal/helper/docker" containerRuntime "github.com/dyrector-io/dyrectorio/golang/internal/runtime/container" - "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/config" ) -func PreflightChecks(cfg *config.Configuration) { +func PreflightChecks() { ctx := context.Background() cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) diff --git a/golang/pkg/dagent/utils/release.go b/golang/pkg/dagent/utils/release.go index 847ae0957..52c148dd8 100644 --- a/golang/pkg/dagent/utils/release.go +++ b/golang/pkg/dagent/utils/release.go @@ -69,10 +69,8 @@ func DraftRelease(instance string, versionData v1.VersionData, deployResponse v1 Str("newPath", backupFilePath). Msg("Existing release file backup failed, overwriting existing release file") } - } else if errors.Is(err, os.ErrNotExist) { + } else if !errors.Is(err, os.ErrNotExist) { // nothing on path -> creating release file - // no-op - } else { // file may or may not exist - Schrodinger -> something is really-really not gud log.Panic().Stack().Err(err).Send() } @@ -102,21 +100,20 @@ func GetVersions(instance string, cfg *config.Configuration) ([]ReleaseDoc, erro for i := range files { if !strings.HasSuffix(files[i].Name(), ".yml") || files[i].IsDir() { continue - } else { - content, err := os.ReadFile(filepath.Clean(path.Join(releaseDirPath, files[i].Name()))) - if err != nil { - log.Error().Stack().Err(err).Str("instance", instance).Str("path", files[i].Name()).Msg("Release read error") - continue - } - - release := ReleaseDoc{} - - err = yaml.Unmarshal(content, &release) - if err != nil { - log.Error().Stack().Err(err).Send() - } - releases = append(releases, release) } + content, err := os.ReadFile(filepath.Clean(path.Join(releaseDirPath, files[i].Name()))) + if err != nil { + log.Error().Stack().Err(err).Str("instance", instance).Str("path", files[i].Name()).Msg("Release read error") + continue + } + + release := ReleaseDoc{} + + err = yaml.Unmarshal(content, &release) + if err != nil { + log.Error().Stack().Err(err).Send() + } + releases = append(releases, release) } return releases, nil diff --git a/golang/pkg/dagent/utils/traefik.go b/golang/pkg/dagent/utils/traefik.go index 753f49445..7dd81eed2 100644 --- a/golang/pkg/dagent/utils/traefik.go +++ b/golang/pkg/dagent/utils/traefik.go @@ -8,12 +8,16 @@ import ( v1 "github.com/dyrector-io/dyrectorio/golang/api/v1" "github.com/dyrector-io/dyrectorio/golang/internal/domain" "github.com/dyrector-io/dyrectorio/golang/internal/util" + "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/config" ) const TraefikTrue = "true" -var ErrInsufficientRoutingRules = errors.New("no enough configuration was provided for the container to be routable") +var ( + ErrInsufficientRoutingRules = errors.New("no enough configuration was provided for the container to be routable") + ErrExposedPortNotFound = errors.New("selected port was provided as exposed port") +) // generating container labels for traefik // if Expose is provided we bind 80 and the given (domainName or (containerName + prefix)) + RootDomain @@ -41,6 +45,16 @@ func GetTraefikLabels( labels["traefik.http.routers."+serviceName+".middlewares"] = serviceName + "-stripper" } + if containerConfig.IngressPort != 0 { + if !util.ContainsMatcher(containerConfig.Ports, + container.PortBinding{ExposedPort: containerConfig.IngressPort}, func(i1, i2 container.PortBinding) bool { + return i1.ExposedPort == i2.ExposedPort + }) { + return nil, ErrExposedPortNotFound + } + labels["traefik.http.services."+serviceName+".loadbalancer.server.port"] = fmt.Sprint(containerConfig.IngressPort) + } + if containerConfig.ExposeTLS { labels["traefik.http.routers."+serviceName+"-secure.entrypoints"] = "websecure" if containerConfig.IngressStripPath && containerConfig.IngressPath != "" { diff --git a/golang/pkg/dagent/utils/traefik_test.go b/golang/pkg/dagent/utils/traefik_test.go index c79f26532..f01262846 100644 --- a/golang/pkg/dagent/utils/traefik_test.go +++ b/golang/pkg/dagent/utils/traefik_test.go @@ -220,3 +220,50 @@ func TestLocalhost(t *testing.T) { assert.Nil(t, err) assert.Equal(t, expected, labels) } + +func TestGetTraefikLabelsWithPorts(t *testing.T) { + instanceConfig := &v1.InstanceConfig{ + ContainerPreName: "prefix", + } + containerConfig := &v1.ContainerConfig{ + Container: "name", + Ports: []container.PortBinding{ + {ExposedPort: 8888, PortBinding: pointer.ToUint16(1)}, + }, + IngressName: "domain.test", + IngressHost: "root.domain", + IngressPort: 8888, + } + cfg := &dagentConfig.Configuration{} + + expected := map[string]string{ + "traefik.enable": "true", + "traefik.http.routers.prefix-name.rule": "Host(`domain.test.root.domain`)", + "traefik.http.routers.prefix-name.entrypoints": "web", + "traefik.http.services.prefix-name.loadbalancer.server.port": "8888", + } + + labels, err := GetTraefikLabels(instanceConfig, containerConfig, cfg) + assert.Nil(t, err) + assert.Equal(t, expected, labels) +} + +func TestGetTraefikLabelsWithInvalidPort(t *testing.T) { + instanceConfig := &v1.InstanceConfig{ + ContainerPreName: "prefix", + } + containerConfig := &v1.ContainerConfig{ + Container: "name", + Ports: []container.PortBinding{ + {ExposedPort: 8888, PortBinding: pointer.ToUint16(1)}, + }, + IngressName: "domain.test", + IngressHost: "root.domain", + IngressPort: 8889, + } + cfg := &dagentConfig.Configuration{} + + labels, err := GetTraefikLabels(instanceConfig, containerConfig, cfg) + assert.ErrorIs(t, err, ErrExposedPortNotFound) + assert.Nil(t, labels) +} diff --git a/images/builder-golang/Dockerfile b/images/builder-golang/Dockerfile index 4ed8561b7..060897cbf 100644 --- a/images/builder-golang/Dockerfile +++ b/images/builder-golang/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/golang:1.20-alpine3.17 +FROM docker.io/library/golang:1.20-alpine3.18 ENV GOLANGCI_LINT_CACHE $GOPATH/cache ENV GOCACHE $GOPATH/cache diff --git a/protobuf/go/agent/agent.pb.go b/protobuf/go/agent/agent.pb.go index 1504bb446..60936229b 100644 --- a/protobuf/go/agent/agent.pb.go +++ b/protobuf/go/agent/agent.pb.go @@ -1397,7 +1397,7 @@ type CraneContainerConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DeploymentStatregy *common.DeploymentStrategy `protobuf:"varint,100,opt,name=deploymentStatregy,proto3,enum=common.DeploymentStrategy,oneof" json:"deploymentStatregy,omitempty"` + DeploymentStrategy *common.DeploymentStrategy `protobuf:"varint,100,opt,name=deploymentStrategy,proto3,enum=common.DeploymentStrategy,oneof" json:"deploymentStrategy,omitempty"` HealthCheckConfig *common.HealthCheckConfig `protobuf:"bytes,101,opt,name=healthCheckConfig,proto3,oneof" json:"healthCheckConfig,omitempty"` ResourceConfig *common.ResourceConfig `protobuf:"bytes,102,opt,name=resourceConfig,proto3,oneof" json:"resourceConfig,omitempty"` ProxyHeaders *bool `protobuf:"varint,103,opt,name=proxyHeaders,proto3,oneof" json:"proxyHeaders,omitempty"` @@ -1441,9 +1441,9 @@ func (*CraneContainerConfig) Descriptor() ([]byte, []int) { return file_protobuf_proto_agent_proto_rawDescGZIP(), []int{18} } -func (x *CraneContainerConfig) GetDeploymentStatregy() common.DeploymentStrategy { - if x != nil && x.DeploymentStatregy != nil { - return *x.DeploymentStatregy +func (x *CraneContainerConfig) GetDeploymentStrategy() common.DeploymentStrategy { + if x != nil && x.DeploymentStrategy != nil { + return *x.DeploymentStrategy } return common.DeploymentStrategy(0) } @@ -2469,11 +2469,11 @@ var file_protobuf_proto_agent_proto_rawDesc = []byte{ 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xc3, 0x06, 0x0a, 0x14, 0x43, 0x72, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x4f, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x72, 0x65, 0x67, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, + 0x4f, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x72, 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x4c, 0x0a, 0x11, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, @@ -2512,7 +2512,7 @@ var file_protobuf_proto_agent_proto_rawDesc = []byte{ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x72, 0x65, 0x67, 0x79, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, @@ -2803,7 +2803,7 @@ var file_protobuf_proto_agent_proto_depIdxs = []int32{ 45, // 25: agent.DagentContainerConfig.restartPolicy:type_name -> common.RestartPolicy 46, // 26: agent.DagentContainerConfig.networkMode:type_name -> common.NetworkMode 37, // 27: agent.DagentContainerConfig.labels:type_name -> agent.DagentContainerConfig.LabelsEntry - 47, // 28: agent.CraneContainerConfig.deploymentStatregy:type_name -> common.DeploymentStrategy + 47, // 28: agent.CraneContainerConfig.deploymentStrategy:type_name -> common.DeploymentStrategy 48, // 29: agent.CraneContainerConfig.healthCheckConfig:type_name -> common.HealthCheckConfig 49, // 30: agent.CraneContainerConfig.resourceConfig:type_name -> common.ResourceConfig 16, // 31: agent.CraneContainerConfig.annotations:type_name -> agent.Marker diff --git a/protobuf/go/common/common.pb.go b/protobuf/go/common/common.pb.go index f62b6b2c6..8896dbc44 100644 --- a/protobuf/go/common/common.pb.go +++ b/protobuf/go/common/common.pb.go @@ -253,7 +253,7 @@ type DeploymentStrategy int32 const ( DeploymentStrategy_DEPLOYMENT_STRATEGY_UNSPECIFIED DeploymentStrategy = 0 DeploymentStrategy_RECREATE DeploymentStrategy = 1 - DeploymentStrategy_ROLLING DeploymentStrategy = 2 + DeploymentStrategy_ROLLING_UPDATE DeploymentStrategy = 2 ) // Enum value maps for DeploymentStrategy. @@ -261,12 +261,12 @@ var ( DeploymentStrategy_name = map[int32]string{ 0: "DEPLOYMENT_STRATEGY_UNSPECIFIED", 1: "RECREATE", - 2: "ROLLING", + 2: "ROLLING_UPDATE", } DeploymentStrategy_value = map[string]int32{ "DEPLOYMENT_STRATEGY_UNSPECIFIED": 0, "RECREATE": 1, - "ROLLING": 2, + "ROLLING_UPDATE": 2, } ) @@ -1021,6 +1021,7 @@ type Routing struct { Path *string `protobuf:"bytes,101,opt,name=path,proto3,oneof" json:"path,omitempty"` StripPath *bool `protobuf:"varint,102,opt,name=stripPath,proto3,oneof" json:"stripPath,omitempty"` UploadLimit *string `protobuf:"bytes,103,opt,name=uploadLimit,proto3,oneof" json:"uploadLimit,omitempty"` + Port *uint32 `protobuf:"varint,104,opt,name=port,proto3,oneof" json:"port,omitempty"` } func (x *Routing) Reset() { @@ -1083,6 +1084,13 @@ func (x *Routing) GetUploadLimit() string { return "" } +func (x *Routing) GetPort() uint32 { + if x != nil && x.Port != nil { + return *x.Port + } + return 0 +} + type ConfigContainer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1788,7 +1796,7 @@ var file_protobuf_proto_common_proto_rawDesc = []byte{ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x27, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6c, - 0x6f, 0x67, 0x22, 0xbb, 0x01, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, + 0x6f, 0x67, 0x22, 0xdd, 0x01, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, @@ -1796,153 +1804,155 @@ var file_protobuf_proto_common_proto_rawDesc = []byte{ 0x68, 0x18, 0x66, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x09, 0x73, 0x74, 0x72, 0x69, 0x70, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x67, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0b, - 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, - 0x0a, 0x07, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x70, 0x50, 0x61, 0x74, 0x68, - 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x22, 0x71, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x64, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x66, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x88, - 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, - 0x6f, 0x62, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x6c, 0x69, 0x76, - 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, - 0x0e, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x18, - 0x66, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, - 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x67, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x03, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x62, 0x65, - 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x10, 0x0a, 0x0e, - 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x42, 0x11, - 0x0a, 0x0f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, - 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, - 0x62, 0x65, 0x22, 0x51, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x15, - 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x63, - 0x70, 0x75, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, - 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, - 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x63, 0x70, 0x75, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x06, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x73, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x01, 0x52, 0x08, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x73, 0x22, 0x32, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8d, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, + 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x17, + 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x68, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, + 0x73, 0x74, 0x72, 0x69, 0x70, 0x50, 0x61, 0x74, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x6f, + 0x72, 0x74, 0x22, 0x71, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x64, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x66, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x17, 0x0a, 0x04, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, + 0x74, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, + 0x50, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x6c, + 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x2b, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, + 0x65, 0x18, 0x66, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x67, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x03, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, + 0x62, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x10, + 0x0a, 0x0e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x62, 0x65, + 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, + 0x72, 0x6f, 0x62, 0x65, 0x22, 0x51, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x15, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x03, 0x63, 0x70, 0x75, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x63, 0x70, 0x75, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x06, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x06, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x01, 0x52, + 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x22, 0x32, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8d, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x61, + 0x73, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x2d, 0x0a, 0x09, 0x55, 0x6e, 0x69, 0x71, + 0x75, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x65, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x41, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x61, 0x73, 0x4b, - 0x65, 0x79, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4b, 0x65, - 0x79, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x2d, 0x0a, 0x09, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, - 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x41, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, - 0x38, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x65, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x17, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x12, 0x19, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0xca, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x08, 0x0a, - 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2a, 0x64, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, 0x4e, - 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, - 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x49, 0x54, 0x49, - 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x10, 0x03, - 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x8f, 0x01, - 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x49, - 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, - 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, - 0x46, 0x55, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, - 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x4f, 0x42, 0x53, 0x4f, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, - 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x06, 0x2a, - 0x4b, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1c, - 0x0a, 0x18, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, - 0x42, 0x52, 0x49, 0x44, 0x47, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, - 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x06, 0x2a, 0x6e, 0x0a, 0x0d, - 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x16, 0x0a, - 0x12, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, - 0x45, 0x44, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x4e, 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, - 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, - 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4c, 0x45, - 0x53, 0x53, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x05, 0x2a, 0x54, 0x0a, 0x12, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x17, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x12, 0x38, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x65, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x17, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0xca, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, + 0x08, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2a, 0x64, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x43, + 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x49, + 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, + 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x04, 0x2a, + 0x8f, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50, 0x41, + 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, + 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x55, 0x43, 0x43, 0x45, + 0x53, 0x53, 0x46, 0x55, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, + 0x44, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x4f, 0x42, 0x53, 0x4f, 0x4c, 0x45, 0x54, 0x45, 0x10, + 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, + 0x06, 0x2a, 0x4b, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x1c, 0x0a, 0x18, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x4d, 0x4f, 0x44, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, + 0x0a, 0x06, 0x42, 0x52, 0x49, 0x44, 0x47, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, + 0x53, 0x54, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x06, 0x2a, 0x6e, + 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x16, 0x0a, 0x12, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, + 0x49, 0x4e, 0x45, 0x44, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x4e, 0x4f, 0x10, 0x02, 0x12, 0x0e, + 0x0a, 0x0a, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x03, 0x12, 0x0a, + 0x0a, 0x06, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, + 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x05, 0x2a, 0x5b, + 0x0a, 0x12, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x61, + 0x74, 0x65, 0x67, 0x79, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, + 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x4c, 0x4c, 0x49, + 0x4e, 0x47, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x2a, 0x55, 0x0a, 0x0a, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4f, 0x4c, + 0x55, 0x4d, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x4f, 0x10, 0x01, 0x12, 0x07, + 0x0a, 0x03, 0x52, 0x57, 0x4f, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x57, 0x58, 0x10, 0x03, + 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x45, 0x4d, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4d, 0x50, + 0x10, 0x05, 0x2a, 0xdf, 0x01, 0x0a, 0x0a, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, + 0x0a, 0x0c, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x01, + 0x12, 0x14, 0x0a, 0x10, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x43, 0x50, 0x4c, 0x4f, 0x47, + 0x53, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0d, + 0x0a, 0x09, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x0a, 0x0a, + 0x06, 0x53, 0x59, 0x53, 0x4c, 0x4f, 0x47, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x4a, 0x4f, 0x55, + 0x52, 0x4e, 0x41, 0x4c, 0x44, 0x10, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x45, 0x4c, 0x46, 0x10, + 0x08, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4c, 0x55, 0x45, 0x4e, 0x54, 0x44, 0x10, 0x09, 0x12, 0x0b, + 0x0a, 0x07, 0x41, 0x57, 0x53, 0x4c, 0x4f, 0x47, 0x53, 0x10, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x53, + 0x50, 0x4c, 0x55, 0x4e, 0x4b, 0x10, 0x0b, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x54, 0x57, 0x4c, 0x4f, + 0x47, 0x53, 0x10, 0x0c, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x4f, 0x47, 0x45, 0x4e, 0x54, 0x52, 0x49, + 0x45, 0x53, 0x10, 0x0d, 0x2a, 0x5f, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, 0x52, 0x45, - 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x4f, 0x4c, 0x4c, 0x49, 0x4e, 0x47, - 0x10, 0x02, 0x2a, 0x55, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x06, 0x0a, - 0x02, 0x52, 0x4f, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x57, 0x4f, 0x10, 0x02, 0x12, 0x07, - 0x0a, 0x03, 0x52, 0x57, 0x58, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x45, 0x4d, 0x10, 0x04, - 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4d, 0x50, 0x10, 0x05, 0x2a, 0xdf, 0x01, 0x0a, 0x0a, 0x44, 0x72, - 0x69, 0x76, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x52, 0x49, 0x56, - 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x45, - 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x52, 0x49, 0x56, 0x45, - 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, - 0x07, 0x47, 0x43, 0x50, 0x4c, 0x4f, 0x47, 0x53, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, - 0x43, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x49, - 0x4c, 0x45, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53, 0x4c, 0x4f, 0x47, 0x10, 0x06, - 0x12, 0x0c, 0x0a, 0x08, 0x4a, 0x4f, 0x55, 0x52, 0x4e, 0x41, 0x4c, 0x44, 0x10, 0x07, 0x12, 0x08, - 0x0a, 0x04, 0x47, 0x45, 0x4c, 0x46, 0x10, 0x08, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4c, 0x55, 0x45, - 0x4e, 0x54, 0x44, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x57, 0x53, 0x4c, 0x4f, 0x47, 0x53, - 0x10, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x55, 0x4e, 0x4b, 0x10, 0x0b, 0x12, 0x0b, - 0x0a, 0x07, 0x45, 0x54, 0x57, 0x4c, 0x4f, 0x47, 0x53, 0x10, 0x0c, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, - 0x4f, 0x47, 0x45, 0x4e, 0x54, 0x52, 0x49, 0x45, 0x53, 0x10, 0x0d, 0x2a, 0x5f, 0x0a, 0x0e, 0x45, - 0x78, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1f, 0x0a, - 0x1b, 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, - 0x0a, 0x07, 0x4e, 0x4f, 0x4e, 0x45, 0x5f, 0x45, 0x53, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, - 0x58, 0x50, 0x4f, 0x53, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x58, 0x50, 0x4f, 0x53, - 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x54, 0x4c, 0x53, 0x10, 0x03, 0x2a, 0x79, 0x0a, 0x12, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, - 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, - 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x02, - 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, - 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x03, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x79, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x69, - 0x6f, 0x2f, 0x64, 0x79, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x4e, 0x45, 0x5f, + 0x45, 0x53, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x10, 0x02, + 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, + 0x54, 0x4c, 0x53, 0x10, 0x03, 0x2a, 0x79, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x1f, 0x43, + 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, + 0x4e, 0x45, 0x52, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x43, 0x4f, + 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x53, + 0x54, 0x41, 0x52, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x03, + 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, + 0x79, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x69, 0x6f, 0x2f, 0x64, 0x79, 0x72, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, + 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protobuf/proto/agent.proto b/protobuf/proto/agent.proto index bd19ceb1f..4be39cace 100644 --- a/protobuf/proto/agent.proto +++ b/protobuf/proto/agent.proto @@ -178,7 +178,7 @@ message Metrics { } message CraneContainerConfig { - optional common.DeploymentStrategy deploymentStatregy = 100; + optional common.DeploymentStrategy deploymentStrategy = 100; optional common.HealthCheckConfig healthCheckConfig = 101; optional common.ResourceConfig resourceConfig = 102; optional bool proxyHeaders = 103; diff --git a/protobuf/proto/common.proto b/protobuf/proto/common.proto index 5117c0847..70345e76a 100644 --- a/protobuf/proto/common.proto +++ b/protobuf/proto/common.proto @@ -96,7 +96,7 @@ enum RestartPolicy { enum DeploymentStrategy { DEPLOYMENT_STRATEGY_UNSPECIFIED = 0; RECREATE = 1; - ROLLING = 2; + ROLLING_UPDATE = 2; } enum VolumeType { @@ -137,6 +137,7 @@ message Routing { optional string path = 101; optional bool stripPath = 102; optional string uploadLimit = 103; + optional uint32 port = 104; } message ConfigContainer { diff --git a/web/crux-ui/e2e/utils/websocket-match.ts b/web/crux-ui/e2e/utils/websocket-match.ts index a69d2d51f..535b0a257 100644 --- a/web/crux-ui/e2e/utils/websocket-match.ts +++ b/web/crux-ui/e2e/utils/websocket-match.ts @@ -1,6 +1,6 @@ -export const wsPatchMatchPorts = (internalPort: string, externalPort: string) => (payload: any) => { +export const wsPatchMatchPorts = (internalPort: string, externalPort?: string) => (payload: any) => { const internal = Number.parseInt(internalPort, 10) - const external = Number.parseInt(externalPort, 10) + const external = externalPort ? Number.parseInt(externalPort, 10) : null return payload.config?.ports?.some(it => it.internal === internal && it.external === external) } @@ -22,52 +22,41 @@ export const wsPatchMatchPortRange = ) } -export const wsPatchMatchSecret = (secret: string, required: boolean) => (payload: any) => { - return payload.config?.secrets?.some(it => it.key === secret && it.required === required) -} +export const wsPatchMatchSecret = (secret: string, required: boolean) => (payload: any) => + payload.config?.secrets?.some(it => it.key === secret && it.required === required) -export const wsPatchMatchCommand = (command: string) => (payload: any) => { - return payload.config?.commands?.some(it => it.key === command) -} +export const wsPatchMatchCommand = (command: string) => (payload: any) => + payload.config?.commands?.some(it => it.key === command) -export const wsPatchMatchArgument = (argument: string) => (payload: any) => { - return payload.config?.args?.some(it => it.key === argument) -} +export const wsPatchMatchArgument = (argument: string) => (payload: any) => + payload.config?.args?.some(it => it.key === argument) export const wsPatchMatchRouting = - (domain: string, path: string, uploadLimit: string, stripPath: boolean) => (payload: any) => { - let routing = payload.config?.routing + (domain: string, path: string, uploadLimit: string, stripPath: boolean, port: number) => (payload: any) => { + const routing = payload.config?.routing return ( routing?.uploadLimit === uploadLimit && routing?.domain === domain && routing?.path === path && - routing?.stripPath === stripPath + routing?.stripPath === stripPath && + routing?.port === port ) } -export const wsPatchMatchContainerName = (name: string) => (payload: any) => { - return payload.config?.name === name -} +export const wsPatchMatchContainerName = (name: string) => (payload: any) => payload.config?.name === name -export const wsPatchMatchUser = (user: number) => (payload: any) => { - return payload.config?.user === user -} +export const wsPatchMatchUser = (user: number) => (payload: any) => payload.config?.user === user -export const wsPatchMatchExpose = (expose: string) => (payload: any) => { - return payload.config?.expose === expose -} +export const wsPatchMatchExpose = (expose: string) => (payload: any) => payload.config?.expose === expose -export const wsPatchMatchTTY = (tty: boolean) => (payload: any) => { - return payload.config?.tty === tty -} +export const wsPatchMatchTTY = (tty: boolean) => (payload: any) => payload.config?.tty === tty -export const wsPatchMatchEnvironment = (key: string, value: string) => (payload: any) => { - return payload.config?.environment?.some(it => it.key === key && it.value === value) -} +export const wsPatchMatchEnvironment = (key: string, value: string) => (payload: any) => + payload.config?.environment?.some(it => it.key === key && it.value === value) export const wsPatchMatchConfigContainer = (image: string, volume: string, path: string, keepFiles: boolean) => (payload: any) => { - let conf = payload.config?.configContainer + const conf = payload.config?.configContainer return conf?.image === image && conf?.volume === volume && conf?.path === path && conf?.keepFiles === keepFiles } @@ -82,8 +71,8 @@ export const wsPatchMatchInitContainer = envKey: string, envVal: string, ) => - (payload: any) => { - return payload.config?.initContainers?.some( + (payload: any) => + payload.config?.initContainers?.some( it => it.name === name && it.image === image && @@ -92,65 +81,46 @@ export const wsPatchMatchInitContainer = it.command?.some(cmds => cmds.key === cmd) && it.environment?.some(env => env.key === envKey && env.value === envVal), ) - } -export const wsPatchMatchVolume = (name: string, size: string, path: string, volClass: string) => (payload: any) => { - return payload.config?.volumes?.some( - it => it.name === name && it.path === path && it.size === size && it.class === volClass, - ) -} +export const wsPatchMatchVolume = (name: string, size: string, path: string, volClass: string) => (payload: any) => + payload.config?.volumes?.some(it => it.name === name && it.path === path && it.size === size && it.class === volClass) export const wsPatchMatchStorage = (storageId: string, bucketPath: string, volume: string) => (payload: any) => { - let storage = payload.config?.storage + const storage = payload.config?.storage return storage?.storageId === storageId && storage?.bucket === bucketPath && storage?.path === volume } -export const wsPatchMatchLogConfig = (driver: string, key: string, value: string) => (payload: any) => { - return ( - payload.config?.logConfig?.driver === driver && - payload.config?.logConfig?.options?.some(opts => opts.key === key && opts.value === value) - ) -} +export const wsPatchMatchLogConfig = (driver: string, key: string, value: string) => (payload: any) => + payload.config?.logConfig?.driver === driver && + payload.config?.logConfig?.options?.some(opts => opts.key === key && opts.value === value) -export const wsPatchMatchRestartPolicy = (policy: string) => (payload: any) => { - return payload.config?.restartPolicy === policy -} +export const wsPatchMatchRestartPolicy = (policy: string) => (payload: any) => payload.config?.restartPolicy === policy -export const wsPatchMatchNetworkMode = (mode: string) => (payload: any) => { - return payload.config?.networkMode === mode -} +export const wsPatchMatchNetworkMode = (mode: string) => (payload: any) => payload.config?.networkMode === mode -export const wsPatchMatchNetwork = (network: string) => (payload: any) => { - return payload.config?.networks?.some(it => it.key === network) -} +export const wsPatchMatchNetwork = (network: string) => (payload: any) => + payload.config?.networks?.some(it => it.key === network) -export const wsPatchMatchDockerLabel = (key: string, value: string) => (payload: any) => { - return payload.config?.dockerLabels?.some(it => it.key === key && it.value === value) -} +export const wsPatchMatchDockerLabel = (key: string, value: string) => (payload: any) => + payload.config?.dockerLabels?.some(it => it.key === key && it.value === value) -export const wsPatchMatchDeploymentStrategy = (strategy: string) => (payload: any) => { - return payload.config?.deploymentStrategy === strategy -} +export const wsPatchMatchDeploymentStrategy = (strategy: string) => (payload: any) => + payload.config?.deploymentStrategy === strategy -export const wsPatchMatchCustomHeader = (header: string) => (payload: any) => { - return payload.config?.customHeaders?.some(it => it.key === header) -} +export const wsPatchMatchCustomHeader = (header: string) => (payload: any) => + payload.config?.customHeaders?.some(it => it.key === header) -export const wsPatchMatchProxyHeader = (proxy: boolean) => (payload: any) => { - return payload.config?.proxyHeaders === proxy -} +export const wsPatchMatchProxyHeader = (proxy: boolean) => (payload: any) => payload.config?.proxyHeaders === proxy -export const wsPatchMatchLoadBalancer = (loadbalancer: boolean) => (payload: any) => { - return payload.config?.useLoadBalancer === loadbalancer -} +export const wsPatchMatchLoadBalancer = (loadbalancer: boolean) => (payload: any) => + payload.config?.useLoadBalancer === loadbalancer -export const wsPatchMatchLBAnnotations = (key: string, value: string) => (payload: any) => { - return payload.config?.extraLBAnnotations?.some(it => it.key === key && it.value === value) -} +export const wsPatchMatchLBAnnotations = (key: string, value: string) => (payload: any) => + payload.config?.extraLBAnnotations?.some(it => it.key === key && it.value === value) export const wsPatchMatchHealthCheck = (port: number, liveness: string, readiness: string, startup: string) => (payload: any) => { - let hc = payload.config?.healthCheckConfig + const hc = payload.config?.healthCheckConfig return ( hc?.port === port && hc?.livenessProbe === liveness && @@ -161,7 +131,7 @@ export const wsPatchMatchHealthCheck = export const wsPatchMatchResourceConfig = (cpuLimits: string, cpuRequests: string, memoryLimits: string, memoryRequests: string) => (payload: any) => { - let rsrc = payload.config?.resourceConfig + const rsrc = payload.config?.resourceConfig return ( rsrc?.limits?.cpu === cpuLimits && rsrc?.limits?.memory === memoryLimits && @@ -170,22 +140,16 @@ export const wsPatchMatchResourceConfig = ) } -export const wsPatchMatchDeploymentLabel = (key: string, value: string) => (payload: any) => { - return payload.config?.labels?.deployment?.some(it => it.key === key && it.value === value) -} -export const wsPatchMatchServiceLabel = (key: string, value: string) => (payload: any) => { - return payload.config?.labels?.service?.some(it => it.key === key && it.value === value) -} -export const wsPatchMatchIngressLabel = (key: string, value: string) => (payload: any) => { - return payload.config?.labels?.ingress?.some(it => it.key === key && it.value === value) -} - -export const wsPatchMatchDeploymentAnnotations = (key: string, value: string) => (payload: any) => { - return payload.config?.annotations?.deployment?.some(it => it.key === key && it.value === value) -} -export const wsPatchMatchServiceAnnotations = (key: string, value: string) => (payload: any) => { - return payload.config?.annotations?.service?.some(it => it.key === key && it.value === value) -} -export const wsPatchMatchIngressAnnotations = (key: string, value: string) => (payload: any) => { - return payload.config?.annotations?.ingress?.some(it => it.key === key && it.value === value) -} +export const wsPatchMatchDeploymentLabel = (key: string, value: string) => (payload: any) => + payload.config?.labels?.deployment?.some(it => it.key === key && it.value === value) +export const wsPatchMatchServiceLabel = (key: string, value: string) => (payload: any) => + payload.config?.labels?.service?.some(it => it.key === key && it.value === value) +export const wsPatchMatchIngressLabel = (key: string, value: string) => (payload: any) => + payload.config?.labels?.ingress?.some(it => it.key === key && it.value === value) + +export const wsPatchMatchDeploymentAnnotations = (key: string, value: string) => (payload: any) => + payload.config?.annotations?.deployment?.some(it => it.key === key && it.value === value) +export const wsPatchMatchServiceAnnotations = (key: string, value: string) => (payload: any) => + payload.config?.annotations?.service?.some(it => it.key === key && it.value === value) +export const wsPatchMatchIngressAnnotations = (key: string, value: string) => (payload: any) => + payload.config?.annotations?.ingress?.some(it => it.key === key && it.value === value) diff --git a/web/crux-ui/e2e/with-login/image-config/common-editor.spec.ts b/web/crux-ui/e2e/with-login/image-config/common-editor.spec.ts index ae9c17f32..a775c1822 100644 --- a/web/crux-ui/e2e/with-login/image-config/common-editor.spec.ts +++ b/web/crux-ui/e2e/with-login/image-config/common-editor.spec.ts @@ -136,7 +136,7 @@ test.describe('Image common config from editor', () => { const internalInput = page.locator('input[placeholder="Internal"]') const externalInput = page.locator('input[placeholder="External"]') - let wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_IMAGE, wsPatchMatchPorts(internal, external)) + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_IMAGE, wsPatchMatchPorts(internal, external)) await internalInput.fill(internal) await externalInput.fill(external) await wsSent @@ -170,7 +170,7 @@ test.describe('Image common config from editor', () => { const externalInputFrom = await page.locator('input[placeholder="From"]').nth(1) const externalInputTo = await page.locator('input[placeholder="To"]').nth(1) - let wsSent = wsPatchSent( + const wsSent = wsPatchSent( ws, wsRoute, WS_TYPE_PATCH_IMAGE, @@ -270,18 +270,31 @@ test.describe('Image common config from editor', () => { const ws = await sock const wsRoute = TEAM_ROUTES.project.versions(projectId).detailsSocket(versionId) + await page.locator('button:has-text("Ports")').click() + + const addPortsButton = await page.locator(`[src="/plus.svg"]:right-of(label:has-text("Ports"))`).first() + await addPortsButton.click() + const internal = '1000' + + const internalInput = page.locator('input[placeholder="Internal"]') + + let wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_IMAGE, wsPatchMatchPorts(internal)) + await internalInput.fill(internal) + await wsSent + await page.locator('button:has-text("Routing")').click() - const domain = 'routing-domain' - const path = 'routing-path.test.com' + const domain = 'routing-domain-example.com' + const path = '/testpath' const uploadLimit = '1024' const stripPath = true + const routedPort = Number.parseInt(internal, 10) - const wsSent = wsPatchSent( + wsSent = wsPatchSent( ws, wsRoute, WS_TYPE_PATCH_IMAGE, - wsPatchMatchRouting(domain, path, uploadLimit, stripPath), + wsPatchMatchRouting(domain, path, uploadLimit, stripPath, routedPort), ) await page.locator('input[placeholder="Domain"]').fill(domain) await page.locator('input[placeholder="Path"]').fill(path) @@ -289,6 +302,7 @@ test.describe('Image common config from editor', () => { await page.getByRole('switch', { checked: false }).click() } await page.locator('input[placeholder="Upload limit"]').fill(uploadLimit) + await page.click(`button:text-is("${routedPort}"):right-of(:text-is("Port"))`) await wsSent await page.reload() diff --git a/web/crux-ui/e2e/with-login/image-config/common-json.spec.ts b/web/crux-ui/e2e/with-login/image-config/common-json.spec.ts index 98c20d1b7..65a85cc50 100644 --- a/web/crux-ui/e2e/with-login/image-config/common-json.spec.ts +++ b/web/crux-ui/e2e/with-login/image-config/common-json.spec.ts @@ -323,20 +323,22 @@ test.describe('Image common config from JSON', () => { const jsonEditorButton = await page.waitForSelector('button:has-text("JSON")') await jsonEditorButton.click() - const domain = 'routing-domain' - const path = 'routing-path.test.com' + const domain = 'routing-domain-example.com' + const path = '/testpath' const uploadLimit = '1024' const stripPath = true + const port = 1000 const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.routing = { domain, path, uploadLimit, stripPath } + json.ports = [{ internal: port, external: null }] + json.routing = { domain, path, uploadLimit, stripPath, port } const wsSent = wsPatchSent( ws, wsRoute, WS_TYPE_PATCH_IMAGE, - wsPatchMatchRouting(domain, path, uploadLimit, stripPath), + wsPatchMatchRouting(domain, path, uploadLimit, stripPath, port), ) await jsonEditor.fill(JSON.stringify(json)) await wsSent @@ -433,8 +435,8 @@ test.describe('Image common config from JSON', () => { const json = JSON.parse(await jsonEditor.inputValue()) json.initContainers = [ { - name: name, - image: image, + name, + image, args: [arg], command: [cmd], environment: { [envKey]: envVal }, @@ -483,7 +485,7 @@ test.describe('Image common config from JSON', () => { const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.volumes = [{ name: name, path: path, type: 'rwo', class: volumeClass, size: size }] + json.volumes = [{ name, path, type: 'rwo', class: volumeClass, size }] const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_IMAGE, wsPatchMatchVolume(name, size, path, volumeClass)) await jsonEditor.fill(JSON.stringify(json)) @@ -521,8 +523,8 @@ test.describe('Image common config from JSON', () => { const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.volumes = [{ name: volumeName, path: path, type: 'rwo', class: volumeClass, size: size }] - json.storage = { storageId: storageId, bucket: bucketPath, path: volumeName } + json.volumes = [{ name: volumeName, path, type: 'rwo', class: volumeClass, size }] + json.storage = { storageId, bucket: bucketPath, path: volumeName } const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_IMAGE, wsPatchMatchStorage(storageId, bucketPath, volumeName)) await jsonEditor.fill(JSON.stringify(json)) diff --git a/web/crux-ui/locales/en/common.json b/web/crux-ui/locales/en/common.json index 6628066ac..e8c4f6294 100644 --- a/web/crux-ui/locales/en/common.json +++ b/web/crux-ui/locales/en/common.json @@ -222,11 +222,6 @@ "description": "There is a protected deployment with the same node and prefix. Continuing the deployment could result in unexpected behaviour. Are you sure want to continue?" }, - "deployProtection": { - "title": "Deployment protection", - "description": "There is a protected deployment with the same node and prefix. Continuing the deployment could result in unexpected behaviour. Are you sure want to continue?" - }, - "errors": { "registryRateLimit": "Rate limit reached, please try again later", "registryUnauthorized": "Unauthorized", diff --git a/web/crux-ui/locales/en/container.json b/web/crux-ui/locales/en/container.json index 6264ebed4..5bc8d0000 100644 --- a/web/crux-ui/locales/en/container.json +++ b/web/crux-ui/locales/en/container.json @@ -11,7 +11,10 @@ "common": { "user": "User", "containerName": "Container name", + "port": "Port", "exposePort": "Expose port", + "default": "Default", + "noInternalPortsDefined": "No internal ports defined.", "portBinding": "Port binding", "volumes": "Volumes", "capabilities": "Capabilities", @@ -103,7 +106,6 @@ "deploymentStrategy": "Deployment strategy", "deploymentStrategies": { "recreate": "Recreate", "rolling": "Rolling" }, "healthCheckConfig": "Health check config", - "port": "Port", "livenessProbe": "Liveness probe", "readinessProbe": "Readiness probe", "startupProbe": "Startup probe", @@ -125,7 +127,6 @@ "metrics": "Metrics", "metricsPath": "Metrics path", "metricsPort": "Metrics port", - "noExternalPortsDefined": "No external ports defined.", "placeholders": { "headerName": "Header name", "cpuUsageExample": "10m", diff --git a/web/crux-ui/src/components/projects/versions/images/config/common-config-section.tsx b/web/crux-ui/src/components/projects/versions/images/config/common-config-section.tsx index 46171c2ac..974ebf534 100644 --- a/web/crux-ui/src/components/projects/versions/images/config/common-config-section.tsx +++ b/web/crux-ui/src/components/projects/versions/images/config/common-config-section.tsx @@ -96,6 +96,8 @@ const CommonConfigSection = (props: CommonConfigSectionProps) => { const resetableConfig = propsResetableConfig ?? propsConfig const config = configType === 'instance' ? mergeConfigs(imageConfig, propsConfig) : propsConfig + const exposedPorts = config.ports?.filter(it => !!it.internal) ?? [] + const onVolumesChanged = (it: ContainerConfigVolume[]) => onChange({ volumes: it, @@ -124,6 +126,16 @@ const CommonConfigSection = (props: CommonConfigSectionProps) => { } } + if (config.routing) { + const routingPort = ports.find(it => it.internal === config.routing.port) + patch = { + ...patch, + routing: { + ...config.routing, + port: routingPort?.internal ?? null, + }, + } + } onChange(patch) } @@ -369,6 +381,33 @@ const CommonConfigSection = (props: CommonConfigSectionProps) => { editorOptions={editorOptions} disabled={disabled} /> +
+ {t('common.port')} + + {exposedPorts.length > 0 ? ( + it.internal)]} + selection={config.routing?.port ?? null} + converter={(it: number | null) => it?.toString() ?? t('common.default')} + onSelectionChange={it => + onChange({ + routing: { + ...config.routing, + port: it, + }, + }) + } + disabled={disabled} + /> + ) : ( + + )} +
)} diff --git a/web/crux-ui/src/components/projects/versions/images/config/crane-config-section.tsx b/web/crux-ui/src/components/projects/versions/images/config/crane-config-section.tsx index a957b66cb..4269c91ef 100644 --- a/web/crux-ui/src/components/projects/versions/images/config/crane-config-section.tsx +++ b/web/crux-ui/src/components/projects/versions/images/config/crane-config-section.tsx @@ -18,12 +18,10 @@ import { CommonConfigDetails, ContainerConfigData, ContainerDeploymentStrategyType, - ContainerPort, CraneConfigDetails, InstanceContainerConfigData, InstanceCraneConfigDetails, mergeConfigs, - portToString, } from '@app/models/container' import { nullify, toNumber } from '@app/utils' import useTranslation from 'next-translate/useTranslation' @@ -79,14 +77,14 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => { const resetableConfig = propsResetableConfig ?? propsConfig const config = configType === 'instance' ? mergeConfigs(imageConfig, propsConfig) : propsConfig - const externalPorts = config.ports?.filter(it => !!it.external) ?? [] + const ports = config.ports?.filter(it => !!it.internal) ?? [] useEffect(() => { - if (config.metrics?.enabled && !config.metrics.port && externalPorts.length > 0) { + if (config.metrics?.enabled && !config.metrics.port && ports.length > 0) { onChange({ metrics: { ...config.metrics, - port: externalPorts[0].external, + port: ports[0].internal, }, }) } @@ -133,7 +131,7 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => {
{ disabled={disabled} /> - {externalPorts.length > 0 ? ( + {ports.length > 0 ? (
{t('crane.metricsPort')} @@ -575,12 +573,11 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => { it.external)} + choices={ports.map(it => it.internal)} selection={config.metrics?.port ?? null} - converter={(it: number | null) => { - const selectedPort = config.ports?.find(port => port.external === it) - return portToString(selectedPort as ContainerPort) - }} + converter={(it: number | null) => + config.ports?.find(port => port.internal === it).internal.toString() + } onSelectionChange={it => onChange({ metrics: { @@ -598,7 +595,7 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => { {t('crane.metricsPort')} - +
)} diff --git a/web/crux-ui/src/models/container.ts b/web/crux-ui/src/models/container.ts index 862a1ae43..97b1281b9 100644 --- a/web/crux-ui/src/models/container.ts +++ b/web/crux-ui/src/models/container.ts @@ -104,6 +104,7 @@ export type ContainerConfigRouting = { path?: string stripPath?: boolean uploadLimit?: string + port?: number } export type ContainerConfigVolume = { diff --git a/web/crux-ui/src/validations/container.ts b/web/crux-ui/src/validations/container.ts index eb663c328..6aebb5494 100644 --- a/web/crux-ui/src/validations/container.ts +++ b/web/crux-ui/src/validations/container.ts @@ -74,17 +74,6 @@ const portNumberBaseRule = yup.number().positive().lessThan(65536) const portNumberOptionalRule = portNumberBaseRule.nullable() const portNumberRule = portNumberBaseRule.nullable().required() -const routingRule = yup - .object() - .shape({ - domain: yup.string().nullable(), - path: yup.string().nullable(), - stripPath: yup.bool().nullable(), - uploadLimit: yup.string().nullable(), - }) - .default({}) - .nullable() - const exposeRule = yup .mixed() .oneOf([...CONTAINER_EXPOSE_STRATEGY_VALUES]) @@ -321,14 +310,33 @@ const markerRule = yup .nullable() .optional() +const routingRule = yup.mixed().when('ports', () => + yup + .object() + .shape({ + domain: yup.string().nullable(), + path: yup + .string() + .nullable() + .optional() + .test('path', 'Should start with a leading "/"', (it: string) => (it ? it.startsWith('/') : true)), + stripPath: yup.bool().nullable(), + uploadLimit: yup.string().nullable(), + port: portNumberRule.nullable().optional().notRequired(), + }) + .nullable() + .optional() + .default(null), +) + const createMetricsPortRule = (ports: ContainerPort[]) => { if (!ports?.length) { return portNumberRule.nullable().optional() } // eslint-disable-next-line no-template-curly-in-string - return portNumberRule.test('metric-port', '${path} is missing the external port definition', value => - value && ports.length > 0 ? ports.some(it => it.external === value) : true, + return portNumberRule.test('metric-port', '${path} is missing the internal port definition', value => + value && ports.length > 0 ? ports.some(it => it.internal === value) : true, ) } diff --git a/web/crux-ui/src/websockets/websocket-client.ts b/web/crux-ui/src/websockets/websocket-client.ts index e9a7aef0b..244ba54c9 100644 --- a/web/crux-ui/src/websockets/websocket-client.ts +++ b/web/crux-ui/src/websockets/websocket-client.ts @@ -241,6 +241,7 @@ class WebSocketClient { const route = this.routes.get(path) if (!route) { this.logger.error(`Route not found: ${path}`) + return } route.onMessage(message) diff --git a/web/crux/assets/install-script/install-k8s.sh.hbr b/web/crux/assets/install-script/install-k8s.sh.hbr index 33398e6d5..73bfeb974 100644 --- a/web/crux/assets/install-script/install-k8s.sh.hbr +++ b/web/crux/assets/install-script/install-k8s.sh.hbr @@ -1,9 +1,6 @@ #!/bin/sh set -e -# migrating away from deployments because of the limited restartPolicy -kubectl delete -n dyrectorio deployment dyrectorio-k8s-agent || true - cat < { } // eslint-disable-next-line no-template-curly-in-string - return portNumberOptionalRule.test('metric-port', '${path} is missing the external port definition', value => - value && ports.length > 0 ? ports.some(it => it.external === value) : true, + return portNumberOptionalRule.test('metric-port', '${path} is missing the internal port definition', value => + value && ports.length > 0 ? ports.some(it => it.internal === value) : true, ) } diff --git a/web/crux/src/grpc/protobuf/proto/agent.ts b/web/crux/src/grpc/protobuf/proto/agent.ts index f7796b084..044a6216c 100644 --- a/web/crux/src/grpc/protobuf/proto/agent.ts +++ b/web/crux/src/grpc/protobuf/proto/agent.ts @@ -268,7 +268,7 @@ export interface Metrics { } export interface CraneContainerConfig { - deploymentStatregy?: DeploymentStrategy | undefined + deploymentStrategy?: DeploymentStrategy | undefined healthCheckConfig?: HealthCheckConfig | undefined resourceConfig?: ResourceConfig | undefined proxyHeaders?: boolean | undefined @@ -1050,8 +1050,8 @@ function createBaseCraneContainerConfig(): CraneContainerConfig { export const CraneContainerConfig = { fromJSON(object: any): CraneContainerConfig { return { - deploymentStatregy: isSet(object.deploymentStatregy) - ? deploymentStrategyFromJSON(object.deploymentStatregy) + deploymentStrategy: isSet(object.deploymentStrategy) + ? deploymentStrategyFromJSON(object.deploymentStrategy) : undefined, healthCheckConfig: isSet(object.healthCheckConfig) ? HealthCheckConfig.fromJSON(object.healthCheckConfig) @@ -1074,9 +1074,9 @@ export const CraneContainerConfig = { toJSON(message: CraneContainerConfig): unknown { const obj: any = {} - message.deploymentStatregy !== undefined && - (obj.deploymentStatregy = - message.deploymentStatregy !== undefined ? deploymentStrategyToJSON(message.deploymentStatregy) : undefined) + message.deploymentStrategy !== undefined && + (obj.deploymentStrategy = + message.deploymentStrategy !== undefined ? deploymentStrategyToJSON(message.deploymentStrategy) : undefined) message.healthCheckConfig !== undefined && (obj.healthCheckConfig = message.healthCheckConfig ? HealthCheckConfig.toJSON(message.healthCheckConfig) diff --git a/web/crux/src/grpc/protobuf/proto/common.ts b/web/crux/src/grpc/protobuf/proto/common.ts index cd2580d54..fdf3eedc3 100644 --- a/web/crux/src/grpc/protobuf/proto/common.ts +++ b/web/crux/src/grpc/protobuf/proto/common.ts @@ -223,7 +223,7 @@ export function restartPolicyToJSON(object: RestartPolicy): string { export enum DeploymentStrategy { DEPLOYMENT_STRATEGY_UNSPECIFIED = 0, RECREATE = 1, - ROLLING = 2, + ROLLING_UPDATE = 2, UNRECOGNIZED = -1, } @@ -236,8 +236,8 @@ export function deploymentStrategyFromJSON(object: any): DeploymentStrategy { case 'RECREATE': return DeploymentStrategy.RECREATE case 2: - case 'ROLLING': - return DeploymentStrategy.ROLLING + case 'ROLLING_UPDATE': + return DeploymentStrategy.ROLLING_UPDATE case -1: case 'UNRECOGNIZED': default: @@ -251,8 +251,8 @@ export function deploymentStrategyToJSON(object: DeploymentStrategy): string { return 'DEPLOYMENT_STRATEGY_UNSPECIFIED' case DeploymentStrategy.RECREATE: return 'RECREATE' - case DeploymentStrategy.ROLLING: - return 'ROLLING' + case DeploymentStrategy.ROLLING_UPDATE: + return 'ROLLING_UPDATE' case DeploymentStrategy.UNRECOGNIZED: default: return 'UNRECOGNIZED' @@ -569,6 +569,7 @@ export interface Routing { path?: string | undefined stripPath?: boolean | undefined uploadLimit?: string | undefined + port?: number | undefined } export interface ConfigContainer { @@ -845,6 +846,7 @@ export const Routing = { path: isSet(object.path) ? String(object.path) : undefined, stripPath: isSet(object.stripPath) ? Boolean(object.stripPath) : undefined, uploadLimit: isSet(object.uploadLimit) ? String(object.uploadLimit) : undefined, + port: isSet(object.port) ? Number(object.port) : undefined, } }, @@ -854,6 +856,7 @@ export const Routing = { message.path !== undefined && (obj.path = message.path) message.stripPath !== undefined && (obj.stripPath = message.stripPath) message.uploadLimit !== undefined && (obj.uploadLimit = message.uploadLimit) + message.port !== undefined && (obj.port = Math.round(message.port)) return obj }, }