From 04e2c94de8df18752747bfcbfc92693015833762 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Wed, 9 Jul 2025 14:33:39 +0200 Subject: [PATCH 1/5] feat(crane): nginx proxy headers security fine-tuning Signed-off-by: Nandor Magyar --- golang/api/v1/deploy.go | 25 +- golang/internal/mapper/grpc.go | 10 +- golang/internal/mapper/grpc_test.go | 6 +- golang/pkg/crane/k8s/configmap.go | 20 + golang/pkg/crane/k8s/deploy_facade.go | 15 +- golang/pkg/crane/k8s/ingress.go | 27 +- protobuf/go/agent/agent.pb.go | 488 +++++++++++----------- protobuf/proto/agent.proto | 5 +- web/crux/proto/agent.proto | 5 +- web/crux/src/grpc/protobuf/proto/agent.ts | 29 +- 10 files changed, 339 insertions(+), 291 deletions(-) diff --git a/golang/api/v1/deploy.go b/golang/api/v1/deploy.go index ecaa87e8d..6ed1603ae 100644 --- a/golang/api/v1/deploy.go +++ b/golang/api/v1/deploy.go @@ -179,17 +179,20 @@ type ContainerConfig struct { Networks []string `json:"networks"` Ports []builder.PortBinding `json:"port" binding:"dive"` PortRanges []builder.PortRangeBinding `json:"portRanges" binding:"dive"` - CustomHeaders []string `json:"customHeaders,omitempty"` - Mounts []string `json:"mount"` - IngressPort uint16 `json:"ingressPort"` - Replicas uint8 `json:"replicas"` - Shared bool `json:"shared"` - UseLoadBalancer bool `json:"useLoadBalancer"` - Expose bool `json:"expose"` - ProxyHeaders bool `json:"proxyHeaders"` - ExposeTLS bool `json:"exposeTls"` - IngressStripPath bool `json:"ingressPathStrip"` - TTY bool `json:"tty"` + // These are CORS headers, providing value will enable CORS and add these headers as extra + CorsHeaders []string `json:"corsHeaders,omitempty"` + // Not all headers are proxied downstream by default, this allows the listed headers to reach backends + ProxyHeaders []string `json:"proxyHeaders,omitempty"` + Mounts []string `json:"mount"` + IngressPort uint16 `json:"ingressPort"` + Replicas uint8 `json:"replicas"` + Shared bool `json:"shared"` + ProxyBuffering bool `json:"proxyBuffering"` + UseLoadBalancer bool `json:"useLoadBalancer"` + Expose bool `json:"expose"` + ExposeTLS bool `json:"exposeTls"` + IngressStripPath bool `json:"ingressPathStrip"` + TTY bool `json:"tty"` } type Metrics struct { diff --git a/golang/internal/mapper/grpc.go b/golang/internal/mapper/grpc.go index 56c2dd11e..c66345af7 100644 --- a/golang/internal/mapper/grpc.go +++ b/golang/internal/mapper/grpc.go @@ -174,7 +174,15 @@ func mapCraneConfig(crane *agent.CraneContainerConfig, containerConfig *v1.Conta containerConfig.DeploymentStrategy = strcase.ToCamel(crane.DeploymentStrategy.String()) if crane.ProxyHeaders != nil { - containerConfig.ProxyHeaders = *crane.ProxyHeaders + containerConfig.ProxyHeaders = crane.ProxyHeaders + } + + if crane.CorsHeaders != nil { + containerConfig.CorsHeaders = crane.CorsHeaders + } + + if crane.ProxyBuffering != nil { + containerConfig.ProxyBuffering = *crane.ProxyBuffering } if crane.UseLoadBalancer != nil { diff --git a/golang/internal/mapper/grpc_test.go b/golang/internal/mapper/grpc_test.go index b9ad41d77..fbbb515e9 100644 --- a/golang/internal/mapper/grpc_test.go +++ b/golang/internal/mapper/grpc_test.go @@ -123,7 +123,6 @@ func testExpectedCommon(req *agent.DeployWorkloadRequest) *v1.DeployImageRequest RestartPolicy: container.RestartPolicyAlways, Networks: []string{"n1", "n2"}, NetworkMode: "BRIDGE", - CustomHeaders: []string(nil), Annotations: v1.Markers{ Deployment: map[string]string{"annot1": "value1"}, Service: map[string]string{"annot2": "value2"}, @@ -144,7 +143,7 @@ func testExpectedCommon(req *agent.DeployWorkloadRequest) *v1.DeployImageRequest Limits: v1.Resources{CPU: "250m", Memory: "512Mi"}, Requests: v1.Resources{CPU: "100m", Memory: "64Mi"}, }, - ProxyHeaders: true, + ProxyHeaders: []string{"ProxyHeader"}, UseLoadBalancer: true, ExtraLBAnnotations: map[string]string{"annotation1": "value1"}, }, @@ -336,9 +335,8 @@ func testCraneConfig() *agent.CraneContainerConfig { sProbe := "/test-startup" port := int32(1234) return &agent.CraneContainerConfig{ - CustomHeaders: []string{"header1", "value1", "header2", "value2"}, ExtraLBAnnotations: map[string]string{"annotation1": "value1"}, - ProxyHeaders: &b, + ProxyHeaders: []string{"ProxyHeader"}, UseLoadBalancer: &b, Labels: &agent.Marker{ Deployment: map[string]string{"label1": "value1"}, diff --git a/golang/pkg/crane/k8s/configmap.go b/golang/pkg/crane/k8s/configmap.go index ebe49f81c..952b098c6 100644 --- a/golang/pkg/crane/k8s/configmap.go +++ b/golang/pkg/crane/k8s/configmap.go @@ -84,6 +84,26 @@ func (cm *configmap) deployConfigMapRuntime(runtimeType v1.RuntimeConfigType, na return nil } +func (cm *configmap) deployIngressProxyHeaders(namespace string, containerName string, headers ...string) error { + client, err := getConfigMapClient(namespace, cm.appConfig) + if err != nil { + return err + } + + headerMap := make(map[string]string) + for _, item := range headers { + headerMap[item] = "" + } + + _, err = client.Apply(cm.ctx, + corev1.ConfigMap(containerName, namespace). + WithData(headerMap), + metaV1.ApplyOptions{FieldManager: cm.appConfig.FieldManagerName, Force: cm.appConfig.ForceOnConflicts}, + ) + + return err +} + // delete related configmaps. note: configmaps being in use are unaffected by this func (cm *configmap) deleteConfigMaps(namespace, name string) error { client, err := getConfigMapClient(namespace, cm.appConfig) diff --git a/golang/pkg/crane/k8s/deploy_facade.go b/golang/pkg/crane/k8s/deploy_facade.go index 31d20edf4..c1de4122a 100644 --- a/golang/pkg/crane/k8s/deploy_facade.go +++ b/golang/pkg/crane/k8s/deploy_facade.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/rs/zerolog/log" + "k8s.io/apimachinery/pkg/api/errors" v1 "github.com/dyrector-io/dyrectorio/golang/api/v1" "github.com/dyrector-io/dyrectorio/golang/internal/dogger" @@ -13,8 +14,6 @@ import ( "github.com/dyrector-io/dyrectorio/golang/internal/util" builder "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" "github.com/dyrector-io/dyrectorio/golang/pkg/crane/config" - - "k8s.io/apimachinery/pkg/api/errors" ) type DeployFacade struct { @@ -213,26 +212,34 @@ func (d *DeployFacade) Deploy() error { } if d.params.ContainerConfig.Expose { + if len(d.params.ContainerConfig.ProxyHeaders) > 0 { + if err := d.configmap.deployIngressProxyHeaders(d.namespace.name, d.params.ContainerConfig.Container, d.params.ContainerConfig.ProxyHeaders...); err != nil { + log.Error().Err(err).Stack().Msg("Error with ingress proxy headers configmap") + } + } + if err := d.ingress.deployIngress( &DeployIngressOptions{ namespace: d.namespace.name, + buffering: d.params.ContainerConfig.ProxyBuffering, containerName: d.params.ContainerConfig.Container, ingressName: d.params.ContainerConfig.IngressName, ingressHost: d.params.ContainerConfig.IngressHost, ingressPath: d.params.ContainerConfig.IngressPath, stripPrefix: d.params.ContainerConfig.IngressStripPath, uploadLimit: d.params.ContainerConfig.IngressUploadLimit, - customHeaders: d.params.ContainerConfig.CustomHeaders, + proxyHeaders: d.params.ContainerConfig.ProxyHeaders, + corsHeaders: d.params.ContainerConfig.CorsHeaders, 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, labels: d.params.ContainerConfig.Labels.Ingress, }, ); err != nil { log.Error().Err(err).Stack().Msg("Error with ingress") } + } return nil diff --git a/golang/pkg/crane/k8s/ingress.go b/golang/pkg/crane/k8s/ingress.go index acc49bbbb..2690739ba 100644 --- a/golang/pkg/crane/k8s/ingress.go +++ b/golang/pkg/crane/k8s/ingress.go @@ -12,12 +12,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1" netv1 "k8s.io/client-go/applyconfigurations/networking/v1" + networking "k8s.io/client-go/kubernetes/typed/networking/v1" "github.com/dyrector-io/dyrectorio/golang/internal/domain" "github.com/dyrector-io/dyrectorio/golang/internal/util" "github.com/dyrector-io/dyrectorio/golang/pkg/crane/config" - - networking "k8s.io/client-go/kubernetes/typed/networking/v1" ) // facade object for ingress management @@ -37,11 +36,12 @@ type DeployIngressOptions struct { ingressPath string uploadLimit string namespace string - customHeaders []string + proxyHeaders []string + corsHeaders []string portList []int32 port uint16 + buffering bool stripPrefix bool - proxyHeaders bool tls bool } @@ -154,8 +154,6 @@ func getTLSConfig(ingressPath, containerName string, enabled bool) *netv1.Ingres } func getIngressAnnotations(opts *DeployIngressOptions) map[string]string { - corsHeaders := []string{} - annotations := map[string]string{ "kubernetes.io/ingress.class": "nginx", } @@ -165,23 +163,18 @@ func getIngressAnnotations(opts *DeployIngressOptions) map[string]string { annotations["cert-manager.io/cluster-issuer"] = "letsencrypt-prod" } - // Add Custom Headers to the CORS Allow Header annotation if presents - if len(opts.customHeaders) > 0 { - corsHeaders = opts.customHeaders + if len(opts.corsHeaders) > 0 { + annotations["nginx.ingress.kubernetes.io/enable-cors"] = "true" + annotations["nginx.ingress.kubernetes.io/cors-allow-headers"] = strings.Join(opts.proxyHeaders, ", ") } - if opts.proxyHeaders { - extraHeaders := []string{"X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Server", "X-Real-IP", "X-Requested-With"} - corsHeaders = append(corsHeaders, extraHeaders...) - - annotations["nginx.ingress.kubernetes.io/enable-cors"] = "true" + if opts.buffering { annotations["nginx.ingress.kubernetes.io/proxy-buffering"] = "on" annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "256k" } - // Add header string to cors-allow-headers if presents any value - if len(corsHeaders) > 0 { - annotations["nginx.ingress.kubernetes.io/cors-allow-headers"] = strings.Join(corsHeaders, ", ") + if len(opts.proxyHeaders) > 0 { + annotations["nginx.ingress.kubernetes.io/proxy-set-headers"] = util.JoinV("/", opts.namespace, opts.containerName) } if opts.uploadLimit != "" { diff --git a/protobuf/go/agent/agent.pb.go b/protobuf/go/agent/agent.pb.go index f0a224dce..cbb82ec2e 100644 --- a/protobuf/go/agent/agent.pb.go +++ b/protobuf/go/agent/agent.pb.go @@ -1582,14 +1582,15 @@ type CraneContainerConfig struct { 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"` + ProxyBuffering *bool `protobuf:"varint,103,opt,name=proxyBuffering,proto3,oneof" json:"proxyBuffering,omitempty"` UseLoadBalancer *bool `protobuf:"varint,104,opt,name=useLoadBalancer,proto3,oneof" json:"useLoadBalancer,omitempty"` Annotations *Marker `protobuf:"bytes,105,opt,name=annotations,proto3,oneof" json:"annotations,omitempty"` Labels *Marker `protobuf:"bytes,106,opt,name=labels,proto3,oneof" json:"labels,omitempty"` Metrics *Metrics `protobuf:"bytes,107,opt,name=metrics,proto3,oneof" json:"metrics,omitempty"` ReplicaCount *int32 `protobuf:"varint,108,opt,name=replicaCount,proto3,oneof" json:"replicaCount,omitempty"` - CustomHeaders []string `protobuf:"bytes,1000,rep,name=customHeaders,proto3" json:"customHeaders,omitempty"` ExtraLBAnnotations map[string]string `protobuf:"bytes,1001,rep,name=extraLBAnnotations,proto3" json:"extraLBAnnotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ProxyHeaders []string `protobuf:"bytes,1003,rep,name=proxyHeaders,proto3" json:"proxyHeaders,omitempty"` + CorsHeaders []string `protobuf:"bytes,1004,rep,name=corsHeaders,proto3" json:"corsHeaders,omitempty"` } func (x *CraneContainerConfig) Reset() { @@ -1645,9 +1646,9 @@ func (x *CraneContainerConfig) GetResourceConfig() *common.ResourceConfig { return nil } -func (x *CraneContainerConfig) GetProxyHeaders() bool { - if x != nil && x.ProxyHeaders != nil { - return *x.ProxyHeaders +func (x *CraneContainerConfig) GetProxyBuffering() bool { + if x != nil && x.ProxyBuffering != nil { + return *x.ProxyBuffering } return false } @@ -1687,16 +1688,23 @@ func (x *CraneContainerConfig) GetReplicaCount() int32 { return 0 } -func (x *CraneContainerConfig) GetCustomHeaders() []string { +func (x *CraneContainerConfig) GetExtraLBAnnotations() map[string]string { if x != nil { - return x.CustomHeaders + return x.ExtraLBAnnotations } return nil } -func (x *CraneContainerConfig) GetExtraLBAnnotations() map[string]string { +func (x *CraneContainerConfig) GetProxyHeaders() []string { if x != nil { - return x.ExtraLBAnnotations + return x.ProxyHeaders + } + return nil +} + +func (x *CraneContainerConfig) GetCorsHeaders() []string { + if x != nil { + return x.CorsHeaders } return nil } @@ -2718,7 +2726,7 @@ var file_protobuf_proto_agent_proto_rawDesc = []byte{ 0x67, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xfd, 0x06, 0x0a, 0x14, 0x43, 0x72, 0x61, 0x6e, 0x65, 0x43, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xa4, 0x07, 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, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, @@ -2734,235 +2742,237 @@ var file_protobuf_proto_agent_proto_rawDesc = []byte{ 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x02, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, - 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x48, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x75, - 0x73, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x68, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x04, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x0b, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x69, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x48, 0x05, - 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, - 0x12, 0x2a, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x48, - 0x06, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x07, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x48, 0x07, 0x52, - 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x6c, 0x20, 0x01, 0x28, - 0x05, 0x48, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0xe8, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x65, - 0x78, 0x74, 0x72, 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0xe9, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x43, 0x72, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x65, - 0x78, 0x74, 0x72, 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 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, 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, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x75, 0x73, - 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x42, 0x0e, 0x0a, - 0x0c, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf2, 0x07, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x66, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x78, 0x70, - 0x6f, 0x73, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x06, 0x65, - 0x78, 0x70, 0x6f, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x07, 0x72, 0x6f, 0x75, 0x74, - 0x69, 0x6e, 0x67, 0x18, 0x67, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x48, 0x01, 0x52, 0x07, 0x72, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x68, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x02, 0x52, 0x0f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x45, 0x0a, 0x0f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x18, 0x69, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x48, 0x03, 0x52, 0x0f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, - 0x6a, 0x20, 0x01, 0x28, 0x03, 0x48, 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x15, 0x0a, 0x03, 0x54, 0x54, 0x59, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, - 0x03, 0x54, 0x54, 0x59, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x69, - 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x6c, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x06, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, - 0x73, 0x18, 0xe8, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0a, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0xe9, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x73, 0x18, 0xea, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, - 0x12, 0x1b, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0xeb, 0x07, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x0a, - 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0xec, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, - 0x67, 0x73, 0x12, 0x50, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, - 0x74, 0x18, 0xed, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, - 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, - 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, - 0xee, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0e, 0x69, 0x6e, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xef, 0x07, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x45, 0x6e, 0x76, - 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 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, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 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, 0x09, 0x0a, 0x07, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, - 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x12, 0x0a, 0x10, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x42, 0x06, 0x0a, - 0x04, 0x5f, 0x54, 0x54, 0x59, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, - 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xa2, 0x03, 0x0a, 0x15, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, - 0x39, 0x0a, 0x06, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x01, 0x52, - 0x06, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x63, 0x72, - 0x61, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x43, 0x72, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x02, 0x52, 0x05, 0x63, 0x72, 0x61, 0x6e, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x74, 0x61, 0x67, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, - 0x75, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x48, 0x04, - 0x52, 0x0c, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x88, 0x01, - 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, - 0x5f, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x63, 0x72, 0x61, 0x6e, - 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x42, 0x0f, - 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x22, - 0x6a, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x07, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, - 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, - 0x0a, 0x0a, 0x08, 0x5f, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, 0x22, 0x44, 0x0a, 0x16, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x47, 0x0a, 0x13, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x12, 0x41, 0x67, - 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, - 0x61, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x22, 0x2b, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x28, 0x0a, - 0x10, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x82, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x39, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 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, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x22, 0x54, 0x0a, 0x17, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x18, 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, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x22, 0x44, 0x0a, 0x16, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x06, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2a, 0x69, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, - 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4c, 0x4f, 0x53, 0x45, - 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x01, - 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x55, 0x43, - 0x54, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x10, - 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x5f, 0x54, 0x4f, 0x4b, 0x45, - 0x4e, 0x10, 0x04, 0x32, 0x9c, 0x05, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, - 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x10, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x30, - 0x01, 0x12, 0x37, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0b, 0x41, 0x62, - 0x6f, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x2d, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x64, 0x12, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x12, 0x44, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x42, 0x0a, 0x12, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, - 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, - 0x12, 0x38, 0x0a, 0x0a, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x10, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x0d, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0c, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x12, 0x20, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, - 0x6f, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x0d, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, - 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, - 0x74, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x42, 0x35, 0x5a, 0x33, 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, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x48, 0x03, 0x52, 0x0e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x88, 0x01, 0x01, 0x12, + 0x2d, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x72, 0x18, 0x68, 0x20, 0x01, 0x28, 0x08, 0x48, 0x04, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x4c, + 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x34, + 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x69, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x61, 0x72, 0x6b, + 0x65, 0x72, 0x48, 0x05, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x6a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x61, 0x72, + 0x6b, 0x65, 0x72, 0x48, 0x06, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x6b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x48, 0x07, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x6c, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x64, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe9, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, + 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x4c, 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0xeb, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x73, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x18, 0xec, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x72, 0x73, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4c, + 0x42, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 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, 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, 0x11, 0x0a, + 0x0f, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x75, 0x73, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x42, + 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf2, 0x07, 0x0a, + 0x15, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x65, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x78, + 0x70, 0x6f, 0x73, 0x65, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x48, 0x00, 0x52, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x2e, 0x0a, 0x07, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x67, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x48, 0x01, 0x52, 0x07, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x88, 0x01, 0x01, 0x12, + 0x46, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x18, 0x68, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x48, 0x02, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x45, 0x0a, 0x0f, 0x69, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x69, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x03, 0x52, 0x0f, 0x69, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, + 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x03, 0x48, 0x04, 0x52, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x54, 0x54, 0x59, 0x18, 0x6b, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x03, 0x54, 0x54, 0x59, 0x88, 0x01, 0x01, 0x12, 0x2f, + 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, + 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, + 0x22, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0xe8, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0b, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x70, 0x6f, + 0x72, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x18, 0xe9, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, + 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0xea, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x73, 0x18, 0xeb, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0xec, 0x07, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x50, 0x0a, 0x0b, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0xed, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x07, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0xee, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x73, 0x12, 0x3d, 0x0a, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x18, 0xef, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, + 0x1a, 0x3e, 0x0a, 0x10, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 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, + 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 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, 0x09, 0x0a, 0x07, + 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x69, 0x6e, 0x67, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, + 0x75, 0x73, 0x65, 0x72, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x54, 0x54, 0x59, 0x42, 0x13, 0x0a, 0x11, + 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x22, 0xa2, 0x03, 0x0a, 0x15, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x06, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x06, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x48, 0x01, 0x52, 0x06, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x88, 0x01, + 0x01, 0x12, 0x36, 0x0a, 0x05, 0x63, 0x72, 0x61, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x61, 0x6e, 0x65, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x02, 0x52, + 0x05, 0x63, 0x72, 0x61, 0x6e, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x41, 0x75, 0x74, 0x68, 0x48, 0x04, 0x52, 0x0c, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x41, 0x75, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x64, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x42, 0x08, + 0x0a, 0x06, 0x5f, 0x63, 0x72, 0x61, 0x6e, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x22, 0x6a, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, + 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, + 0x07, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6f, 0x6e, 0x65, 0x53, 0x68, + 0x6f, 0x74, 0x22, 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x47, 0x0a, 0x13, 0x44, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6a, 0x73, 0x6f, + 0x6e, 0x22, 0x64, 0x0a, 0x12, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x2b, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6c, 0x61, + 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x28, 0x0a, 0x10, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x62, 0x6f, + 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x82, + 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x18, 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, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, + 0x61, 0x69, 0x6c, 0x22, 0x54, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, + 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 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, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x44, 0x0a, 0x16, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2a, + 0x69, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1c, + 0x0a, 0x18, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, + 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x45, 0x4c, 0x46, 0x5f, + 0x44, 0x45, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x48, + 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x56, 0x4f, + 0x4b, 0x45, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x10, 0x04, 0x32, 0x9c, 0x05, 0x0a, 0x05, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, + 0x10, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x35, 0x0a, 0x0b, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x17, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x62, + 0x6f, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x12, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, + 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x21, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x28, 0x01, 0x12, 0x42, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x38, 0x0a, 0x0a, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x30, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4c, 0x6f, 0x67, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x35, 0x5a, 0x33, 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, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protobuf/proto/agent.proto b/protobuf/proto/agent.proto index 4cb60b7df..cb6e2185d 100644 --- a/protobuf/proto/agent.proto +++ b/protobuf/proto/agent.proto @@ -197,15 +197,16 @@ message CraneContainerConfig { optional common.DeploymentStrategy deploymentStrategy = 100; optional common.HealthCheckConfig healthCheckConfig = 101; optional common.ResourceConfig resourceConfig = 102; - optional bool proxyHeaders = 103; + optional bool proxyBuffering = 103; optional bool useLoadBalancer = 104; optional Marker annotations = 105; optional Marker labels = 106; optional Metrics metrics = 107; optional int32 replicaCount = 108; - repeated string customHeaders = 1000; map extraLBAnnotations = 1001; + repeated string proxyHeaders = 1003; + repeated string corsHeaders = 1004; } message CommonContainerConfig { diff --git a/web/crux/proto/agent.proto b/web/crux/proto/agent.proto index 4cb60b7df..cb6e2185d 100644 --- a/web/crux/proto/agent.proto +++ b/web/crux/proto/agent.proto @@ -197,15 +197,16 @@ message CraneContainerConfig { optional common.DeploymentStrategy deploymentStrategy = 100; optional common.HealthCheckConfig healthCheckConfig = 101; optional common.ResourceConfig resourceConfig = 102; - optional bool proxyHeaders = 103; + optional bool proxyBuffering = 103; optional bool useLoadBalancer = 104; optional Marker annotations = 105; optional Marker labels = 106; optional Metrics metrics = 107; optional int32 replicaCount = 108; - repeated string customHeaders = 1000; map extraLBAnnotations = 1001; + repeated string proxyHeaders = 1003; + repeated string corsHeaders = 1004; } message CommonContainerConfig { diff --git a/web/crux/src/grpc/protobuf/proto/agent.ts b/web/crux/src/grpc/protobuf/proto/agent.ts index 93384fb7b..5df980ff7 100644 --- a/web/crux/src/grpc/protobuf/proto/agent.ts +++ b/web/crux/src/grpc/protobuf/proto/agent.ts @@ -283,14 +283,15 @@ export interface CraneContainerConfig { deploymentStrategy?: DeploymentStrategy | undefined healthCheckConfig?: HealthCheckConfig | undefined resourceConfig?: ResourceConfig | undefined - proxyHeaders?: boolean | undefined + proxyBuffering?: boolean | undefined useLoadBalancer?: boolean | undefined annotations?: Marker | undefined labels?: Marker | undefined metrics?: Metrics | undefined replicaCount?: number | undefined - customHeaders: string[] extraLBAnnotations: { [key: string]: string } + proxyHeaders: string[] + corsHeaders: string[] } export interface CraneContainerConfig_ExtraLBAnnotationsEntry { @@ -1115,7 +1116,7 @@ export const DagentContainerConfig_LabelsEntry = { } function createBaseCraneContainerConfig(): CraneContainerConfig { - return { customHeaders: [], extraLBAnnotations: {} } + return { extraLBAnnotations: {}, proxyHeaders: [], corsHeaders: [] } } export const CraneContainerConfig = { @@ -1128,19 +1129,20 @@ export const CraneContainerConfig = { ? HealthCheckConfig.fromJSON(object.healthCheckConfig) : undefined, resourceConfig: isSet(object.resourceConfig) ? ResourceConfig.fromJSON(object.resourceConfig) : undefined, - proxyHeaders: isSet(object.proxyHeaders) ? Boolean(object.proxyHeaders) : undefined, + proxyBuffering: isSet(object.proxyBuffering) ? Boolean(object.proxyBuffering) : undefined, useLoadBalancer: isSet(object.useLoadBalancer) ? Boolean(object.useLoadBalancer) : undefined, annotations: isSet(object.annotations) ? Marker.fromJSON(object.annotations) : undefined, labels: isSet(object.labels) ? Marker.fromJSON(object.labels) : undefined, metrics: isSet(object.metrics) ? Metrics.fromJSON(object.metrics) : undefined, replicaCount: isSet(object.replicaCount) ? Number(object.replicaCount) : undefined, - customHeaders: Array.isArray(object?.customHeaders) ? object.customHeaders.map((e: any) => String(e)) : [], extraLBAnnotations: isObject(object.extraLBAnnotations) ? Object.entries(object.extraLBAnnotations).reduce<{ [key: string]: string }>((acc, [key, value]) => { acc[key] = String(value) return acc }, {}) : {}, + proxyHeaders: Array.isArray(object?.proxyHeaders) ? object.proxyHeaders.map((e: any) => String(e)) : [], + corsHeaders: Array.isArray(object?.corsHeaders) ? object.corsHeaders.map((e: any) => String(e)) : [], } }, @@ -1155,24 +1157,29 @@ export const CraneContainerConfig = { : undefined) message.resourceConfig !== undefined && (obj.resourceConfig = message.resourceConfig ? ResourceConfig.toJSON(message.resourceConfig) : undefined) - message.proxyHeaders !== undefined && (obj.proxyHeaders = message.proxyHeaders) + message.proxyBuffering !== undefined && (obj.proxyBuffering = message.proxyBuffering) message.useLoadBalancer !== undefined && (obj.useLoadBalancer = message.useLoadBalancer) message.annotations !== undefined && (obj.annotations = message.annotations ? Marker.toJSON(message.annotations) : undefined) message.labels !== undefined && (obj.labels = message.labels ? Marker.toJSON(message.labels) : undefined) message.metrics !== undefined && (obj.metrics = message.metrics ? Metrics.toJSON(message.metrics) : undefined) message.replicaCount !== undefined && (obj.replicaCount = Math.round(message.replicaCount)) - if (message.customHeaders) { - obj.customHeaders = message.customHeaders.map(e => e) - } else { - obj.customHeaders = [] - } obj.extraLBAnnotations = {} if (message.extraLBAnnotations) { Object.entries(message.extraLBAnnotations).forEach(([k, v]) => { obj.extraLBAnnotations[k] = v }) } + if (message.proxyHeaders) { + obj.proxyHeaders = message.proxyHeaders.map(e => e) + } else { + obj.proxyHeaders = [] + } + if (message.corsHeaders) { + obj.corsHeaders = message.corsHeaders.map(e => e) + } else { + obj.corsHeaders = [] + } return obj }, } From 7b4e62e29843679b45e944e5b63cf9568333c684 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Mon, 15 Sep 2025 15:47:52 +0200 Subject: [PATCH 2/5] clarify some naming around ingress --- golang/Makefile | 4 ++ golang/pkg/crane/k8s/configmap.go | 2 +- golang/pkg/crane/k8s/deploy_facade.go | 35 ++++++++------ golang/pkg/crane/k8s/ingress.go | 51 +++++++++++--------- golang/pkg/dagent/utils/dockerhelper_test.go | 1 + 5 files changed, 54 insertions(+), 39 deletions(-) diff --git a/golang/Makefile b/golang/Makefile index f4311eab6..3ac19b83c 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -285,3 +285,7 @@ test-cli: .PHONY: coverage coverage: go tool cover -func ./merged.cov + +.PHONY: fieldalign +fieldalign: + fieldalign -fix ./... diff --git a/golang/pkg/crane/k8s/configmap.go b/golang/pkg/crane/k8s/configmap.go index 952b098c6..544965712 100644 --- a/golang/pkg/crane/k8s/configmap.go +++ b/golang/pkg/crane/k8s/configmap.go @@ -84,7 +84,7 @@ func (cm *configmap) deployConfigMapRuntime(runtimeType v1.RuntimeConfigType, na return nil } -func (cm *configmap) deployIngressProxyHeaders(namespace string, containerName string, headers ...string) error { +func (cm *configmap) deployIngressProxyHeaders(namespace, containerName string, headers ...string) error { client, err := getConfigMapClient(namespace, cm.appConfig) if err != nil { return err diff --git a/golang/pkg/crane/k8s/deploy_facade.go b/golang/pkg/crane/k8s/deploy_facade.go index c1de4122a..1fdeb1fce 100644 --- a/golang/pkg/crane/k8s/deploy_facade.go +++ b/golang/pkg/crane/k8s/deploy_facade.go @@ -213,7 +213,10 @@ func (d *DeployFacade) Deploy() error { if d.params.ContainerConfig.Expose { if len(d.params.ContainerConfig.ProxyHeaders) > 0 { - if err := d.configmap.deployIngressProxyHeaders(d.namespace.name, d.params.ContainerConfig.Container, d.params.ContainerConfig.ProxyHeaders...); err != nil { + if err := d.configmap.deployIngressProxyHeaders(d.namespace.name, + d.params.ContainerConfig.Container, + d.params.ContainerConfig.ProxyHeaders..., + ); err != nil { log.Error().Err(err).Stack().Msg("Error with ingress proxy headers configmap") } } @@ -221,27 +224,27 @@ func (d *DeployFacade) Deploy() error { if err := d.ingress.deployIngress( &DeployIngressOptions{ namespace: d.namespace.name, - buffering: d.params.ContainerConfig.ProxyBuffering, containerName: d.params.ContainerConfig.Container, - ingressName: d.params.ContainerConfig.IngressName, - ingressHost: d.params.ContainerConfig.IngressHost, - ingressPath: d.params.ContainerConfig.IngressPath, - stripPrefix: d.params.ContainerConfig.IngressStripPath, - uploadLimit: d.params.ContainerConfig.IngressUploadLimit, - proxyHeaders: d.params.ContainerConfig.ProxyHeaders, - corsHeaders: d.params.ContainerConfig.CorsHeaders, - port: d.params.ContainerConfig.IngressPort, - portList: d.service.portsBound, - tls: d.params.ContainerConfig.ExposeTLS, - annotations: d.params.ContainerConfig.Annotations.Ingress, - labels: d.params.ContainerConfig.Labels.Ingress, + name: d.params.ContainerConfig.IngressName, + routing: routingOptions{ + proxyBuffering: d.params.ContainerConfig.ProxyBuffering, + ingressHost: d.params.ContainerConfig.IngressHost, + ingressPath: d.params.ContainerConfig.IngressPath, + stripPrefix: d.params.ContainerConfig.IngressStripPath, + uploadLimit: d.params.ContainerConfig.IngressUploadLimit, + proxyHeaders: d.params.ContainerConfig.ProxyHeaders, + corsHeaders: d.params.ContainerConfig.CorsHeaders, + port: d.params.ContainerConfig.IngressPort, + portList: d.service.portsBound, + tls: d.params.ContainerConfig.ExposeTLS, + }, + annotations: d.params.ContainerConfig.Annotations.Ingress, + labels: d.params.ContainerConfig.Labels.Ingress, }, ); err != nil { log.Error().Err(err).Stack().Msg("Error with ingress") } - } - return nil } diff --git a/golang/pkg/crane/k8s/ingress.go b/golang/pkg/crane/k8s/ingress.go index 2690739ba..150719efd 100644 --- a/golang/pkg/crane/k8s/ingress.go +++ b/golang/pkg/crane/k8s/ingress.go @@ -27,21 +27,26 @@ type ingress struct { status string } +type routingOptions struct { + ingressHost string + ingressPath string + uploadLimit string + proxyHeaders []string + corsHeaders []string + portList []int32 + port uint16 + proxyBuffering bool + stripPrefix bool + tls bool +} + type DeployIngressOptions struct { annotations map[string]string labels map[string]string containerName string - ingressName string - ingressHost string - ingressPath string - uploadLimit string + name string namespace string - proxyHeaders []string - corsHeaders []string - portList []int32 - port uint16 - buffering bool - stripPrefix bool + routing routingOptions tls bool } @@ -59,29 +64,31 @@ func (ing *ingress) deployIngress(options *DeployIngressOptions) error { log.Error().Err(err).Stack().Msg("Error with ingress client") } - if options.port == 0 && len(options.portList) == 0 { + routing := options.routing + + if routing.port == 0 && len(routing.portList) == 0 { return errors.New("empty ports, nothing to expose") } - routedPort := options.port + routedPort := routing.port if routedPort == 0 { - routedPort = uint16(options.portList[0]) //#nosec G115 + routedPort = uint16(routing.portList[0]) //#nosec G115 } ingressDomain := domain.GetHostRule( &domain.HostRouting{ - Subdomain: options.ingressName, - RootDomain: options.ingressHost, + Subdomain: options.name, + RootDomain: routing.ingressHost, ContainerName: options.containerName, Prefix: options.namespace, DomainFallback: ing.appConfig.RootDomain, }) ingressPath := "/" - if options.ingressPath != "" { - ingressPath = options.ingressPath + if routing.ingressPath != "" { + ingressPath = routing.ingressPath // prefix stripping works in combination with annotations - if options.stripPrefix { + if routing.stripPrefix { split := strings.Split(ingressPath, "/") split = append(split, "?(.*)") @@ -110,7 +117,7 @@ func (ing *ingress) deployIngress(options *DeployIngressOptions) error { spec.WithTLS(tlsConf) } - annot := getIngressAnnotations(options) + annot := getIngressAnnotations(options.namespace, options.containerName, &options.routing) maps.Copy(annot, options.annotations) labels := map[string]string{} @@ -153,7 +160,7 @@ func getTLSConfig(ingressPath, containerName string, enabled bool) *netv1.Ingres return nil } -func getIngressAnnotations(opts *DeployIngressOptions) map[string]string { +func getIngressAnnotations(namespace, name string, opts *routingOptions) map[string]string { annotations := map[string]string{ "kubernetes.io/ingress.class": "nginx", } @@ -168,13 +175,13 @@ func getIngressAnnotations(opts *DeployIngressOptions) map[string]string { annotations["nginx.ingress.kubernetes.io/cors-allow-headers"] = strings.Join(opts.proxyHeaders, ", ") } - if opts.buffering { + if opts.proxyBuffering { annotations["nginx.ingress.kubernetes.io/proxy-buffering"] = "on" annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "256k" } if len(opts.proxyHeaders) > 0 { - annotations["nginx.ingress.kubernetes.io/proxy-set-headers"] = util.JoinV("/", opts.namespace, opts.containerName) + annotations["nginx.ingress.kubernetes.io/proxy-set-headers"] = util.JoinV("/", namespace, name) } if opts.uploadLimit != "" { diff --git a/golang/pkg/dagent/utils/dockerhelper_test.go b/golang/pkg/dagent/utils/dockerhelper_test.go index 1976aea7a..a043ca902 100644 --- a/golang/pkg/dagent/utils/dockerhelper_test.go +++ b/golang/pkg/dagent/utils/dockerhelper_test.go @@ -172,6 +172,7 @@ func TestEventToMessageContainer(t *testing.T) { assert.NoError(t, err) + // if this is failing locally, you have to log in to ghcr.io containerID := *builder.GetContainerID() event := events.Message{ From 2228bb48b13d676e35930251071d088abd2d6bed Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Wed, 24 Sep 2025 13:55:50 +0200 Subject: [PATCH 3/5] feat: implement ui --- web/crux-ui/e2e/utils/websocket-match.ts | 9 ++-- .../kubernetes-editor.spec.ts | 4 +- .../container-config/kubernetes-json.spec.ts | 49 +++++++++++++++---- web/crux-ui/locales/en/container.json | 3 +- .../crane-config-section.tsx | 49 +++++++++++++------ web/crux-ui/src/models/compose.ts | 2 +- web/crux-ui/src/models/container-conflict.ts | 5 +- web/crux-ui/src/models/container-errors.ts | 4 +- web/crux-ui/src/models/container-merge.ts | 15 +++--- web/crux-ui/src/models/container.ts | 8 +-- web/crux-ui/src/models/json-container.ts | 15 +++--- web/crux-ui/src/validations/container.ts | 5 +- .../migration.sql | 3 ++ web/crux/prisma/schema.prisma | 21 ++++---- web/crux/src/app/container/container.const.ts | 3 +- web/crux/src/app/container/container.dto.ts | 8 ++- .../src/app/container/container.mapper.ts | 15 +++--- web/crux/src/app/deploy/deploy.mapper.spec.ts | 23 ++++++--- web/crux/src/app/deploy/deploy.mapper.ts | 5 +- web/crux/src/domain/container-conflict.ts | 5 +- web/crux/src/domain/container-merge.spec.ts | 30 +++++++----- web/crux/src/domain/container-merge.ts | 15 +++--- web/crux/src/domain/container.ts | 8 +-- web/crux/src/domain/validation.ts | 5 +- 24 files changed, 201 insertions(+), 108 deletions(-) create mode 100644 web/crux/prisma/migrations/20250923153744_proxy_headers_rework/migration.sql diff --git a/web/crux-ui/e2e/utils/websocket-match.ts b/web/crux-ui/e2e/utils/websocket-match.ts index 653298038..91cfd0643 100644 --- a/web/crux-ui/e2e/utils/websocket-match.ts +++ b/web/crux-ui/e2e/utils/websocket-match.ts @@ -136,10 +136,13 @@ export const wsPatchMatchDockerLabel = (key: string, value: string) => (payload: export const wsPatchMatchDeploymentStrategy = (strategy: string) => (payload: any) => payload.config?.deploymentStrategy === strategy -export const wsPatchMatchCustomHeader = (header: string) => (payload: any) => - payload.config?.customHeaders?.some(it => it.key === header) +export const wsPatchMatchCorsHeader = (header: string) => (payload: any) => + payload.config?.corsHeaders?.some(it => it.key === header) -export const wsPatchMatchProxyHeader = (proxy: boolean) => (payload: any) => payload.config?.proxyHeaders === proxy +export const wsPatchMatchProxyBuffering = (proxy: boolean) => (payload: any) => payload.config?.proxyBuffering === proxy + +export const wsPatchMatchProxyHeader = (header: string) => (payload: any) => + payload.config?.proxyHeaders?.some(it => it.key === header) export const wsPatchMatchLoadBalancer = (loadbalancer: boolean) => (payload: any) => payload.config?.useLoadBalancer === loadbalancer diff --git a/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts b/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts index f01e3d72c..4170cb045 100644 --- a/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts +++ b/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts @@ -2,7 +2,7 @@ import { expect, Page } from '@playwright/test' import { test } from '../../utils/test.fixture' import { NGINX_TEST_IMAGE_WITH_TAG, TEAM_ROUTES } from 'e2e/utils/common' import { - wsPatchMatchCustomHeader, + wsPatchMatchCorsHeader, wsPatchMatchDeploymentAnnotations, wsPatchMatchDeploymentLabel, wsPatchMatchDeploymentStrategy, @@ -70,7 +70,7 @@ test.describe('Image kubernetes config from editor', () => { const header = 'test-header' const input = page.locator('div:has(label:has-text("CUSTOM HEADERS")) input[placeholder="Header name"]').first() - const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchCustomHeader(header)) + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchCorsHeader(header)) await input.fill(header) await wsSent diff --git a/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts b/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts index a089db185..0aa1afdcc 100644 --- a/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts +++ b/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts @@ -2,7 +2,7 @@ import { HealthCheckProbe, JsonHealthCheckCommandProbe, WS_TYPE_PATCH_CONFIG } f import { expect, Page } from '@playwright/test' import { NGINX_TEST_IMAGE_WITH_TAG, TEAM_ROUTES } from 'e2e/utils/common' import { - wsPatchMatchCustomHeader, + wsPatchMatchCorsHeader, wsPatchMatchDeploymentAnnotations, wsPatchMatchDeploymentLabel, wsPatchMatchDeploymentStrategy, @@ -11,6 +11,7 @@ import { wsPatchMatchJsonHealthCheck, wsPatchMatchLBAnnotations, wsPatchMatchLoadBalancer, + wsPatchMatchProxyBuffering, wsPatchMatchProxyHeader, wsPatchMatchReplicas, wsPatchMatchResourceConfig, @@ -62,8 +63,8 @@ test.describe('Image kubernetes config from JSON', () => { await expect(page.locator(`button.bg-dyo-turquoise:has-text("${strategy}")`)).toBeVisible() }) - test('Custom headers should be saved', async ({ page }) => { - const { imageConfigId } = await setup(page, 'custom-headers-json', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) + test('Cors headers should be saved', async ({ page }) => { + const { imageConfigId } = await setup(page, 'cors-headers-json', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) const sock = waitSocketRef(page) await page.goto(TEAM_ROUTES.containerConfig.details(imageConfigId)) @@ -78,18 +79,45 @@ test.describe('Image kubernetes config from JSON', () => { const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.customHeaders = [header] + json.corsHeaders = [header] - const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchCustomHeader(header)) + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchCorsHeader(header)) await jsonEditor.fill(JSON.stringify(json)) await wsSent await page.reload() - const input = page.locator('div:has(label:has-text("CUSTOM HEADERS")) input[placeholder="Header name"]').first() + const input = page.locator('div:has(label:has-text("CORS HEADERS")) input[placeholder="Header name"]').first() await expect(input).toHaveValue(header) }) + test('Proxy buffering should be saved', async ({ page }) => { + const { imageConfigId } = await setup(page, 'proxy-buffering-json', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) + + const sock = waitSocketRef(page) + await page.goto(TEAM_ROUTES.containerConfig.details(imageConfigId)) + await page.waitForSelector('h2:text-is("Image config")') + const ws = await sock + const wsRoute = TEAM_ROUTES.containerConfig.detailsSocket(imageConfigId) + + const jsonEditorButton = await page.waitForSelector('button:has-text("JSON")') + await jsonEditorButton.click() + + const jsonEditor = await page.locator('textarea') + const json = JSON.parse(await jsonEditor.inputValue()) + json.proxyBuffering = true + + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyBuffering(true)) + await jsonEditor.fill(JSON.stringify(json)) + await wsSent + + await page.reload() + + await expect( + page.locator(':right-of(:text("PROXY BUFFERING"))').getByRole('switch', { checked: true }), + ).toBeVisible() + }) + test('Proxy headers should be saved', async ({ page }) => { const { imageConfigId } = await setup(page, 'proxy-headers-json', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) @@ -99,20 +127,23 @@ test.describe('Image kubernetes config from JSON', () => { const ws = await sock const wsRoute = TEAM_ROUTES.containerConfig.detailsSocket(imageConfigId) + const header = 'test-header' + const jsonEditorButton = await page.waitForSelector('button:has-text("JSON")') await jsonEditorButton.click() const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.proxyHeaders = true + json.customHeaders = [header] - const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyHeader(true)) + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyHeader(header)) await jsonEditor.fill(JSON.stringify(json)) await wsSent await page.reload() - await expect(page.locator(':right-of(:text("PROXY HEADERS"))').getByRole('switch', { checked: true })).toBeVisible() + const input = page.locator('div:has(label:has-text("PROXY HEADERS")) input[placeholder="Header name"]').first() + await expect(input).toHaveValue(header) }) test('Load balancer should be saved', async ({ page }) => { diff --git a/web/crux-ui/locales/en/container.json b/web/crux-ui/locales/en/container.json index 1b929c2ef..953a45590 100644 --- a/web/crux-ui/locales/en/container.json +++ b/web/crux-ui/locales/en/container.json @@ -122,12 +122,13 @@ "livenessProbe": "Liveness probe", "readinessProbe": "Readiness probe", "startupProbe": "Startup probe", - "customHeaders": "Custom headers", + "corsHeaders": "CORS headers", "resourceConfig": "Resource config", "cpu": "Cpu", "memory": "Memory", "limits": "Limits", "requests": "Requests", + "proxyBuffering": "Proxy buffering", "proxyHeaders": "Proxy headers", "loadBalancer": "Load balancer", "useLoadBalancer": "Use load balancer", diff --git a/web/crux-ui/src/components/container-configs/crane-config-section.tsx b/web/crux-ui/src/components/container-configs/crane-config-section.tsx index b546fee72..adaba1a66 100644 --- a/web/crux-ui/src/components/container-configs/crane-config-section.tsx +++ b/web/crux-ui/src/components/container-configs/crane-config-section.tsx @@ -131,20 +131,20 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => { )} - {/* customHeaders */} - {filterContains('customHeaders', selectedFilters) && ( + {/* corsHeaders */} + {filterContains('corsHeaders', selectedFilters) && (
onChange({ customHeaders: it })} + onChange={it => onChange({ corsHeaders: it })} editorOptions={editorOptions} disabled={disabled} - onResetSection={resettableConfig.customHeaders ? () => onResetSection('customHeaders') : null} + onResetSection={resettableConfig.corsHeaders ? () => onResetSection('corsHeaders') : null} /> - +
)} @@ -380,24 +380,41 @@ const CraneConfigSection = (props: CraneConfigSectionProps) => { )} - {/* proxyHeaders */} - {filterContains('proxyHeaders', selectedFilters) && ( + {/* proxyBuffering */} + {filterContains('proxyBuffering', selectedFilters) && (
onResetSection('proxyHeaders')} - error={conflictErrors?.proxyHeaders} + disabled={disabled || !booleanResettable(baseConfig?.proxyBuffering, resettableConfig.proxyBuffering)} + onResetSection={() => onResetSection('proxyBuffering')} + error={conflictErrors?.proxyBuffering} > - {t('crane.proxyHeaders').toUpperCase()} + {t('crane.proxyBuffering').toUpperCase()} onChange({ proxyHeaders: it })} + name="proxyBuffering" + checked={config.proxyBuffering} + onCheckedChange={it => onChange({ proxyBuffering: it })} + disabled={disabled} + /> +
+ )} + + {/* proxyHeaders */} + {filterContains('proxyHeaders', selectedFilters) && ( +
+ onChange({ proxyHeaders: it })} + editorOptions={editorOptions} disabled={disabled} + onResetSection={resettableConfig.proxyHeaders ? () => onResetSection('proxyHeaders') : null} /> +
)} diff --git a/web/crux-ui/src/models/compose.ts b/web/crux-ui/src/models/compose.ts index 3a32f3e68..e4a36de1c 100644 --- a/web/crux-ui/src/models/compose.ts +++ b/web/crux-ui/src/models/compose.ts @@ -272,7 +272,7 @@ export const mapComposeServiceToContainerConfig = ( expose: 'none', capabilities: [], deploymentStrategy: 'recreate', - proxyHeaders: false, + proxyBuffering: false, useLoadBalancer: false, } } diff --git a/web/crux-ui/src/models/container-conflict.ts b/web/crux-ui/src/models/container-conflict.ts index 09e712338..26aac507a 100644 --- a/web/crux-ui/src/models/container-conflict.ts +++ b/web/crux-ui/src/models/container-conflict.ts @@ -78,7 +78,7 @@ export type ConflictedContainerConfigData = { // crane deploymentStrategy?: string[] - proxyHeaders?: string[] + proxyBuffering?: string[] useLoadBalancer?: string[] extraLBAnnotations?: ConflictedUniqueItem[] healthCheckConfig?: string[] @@ -600,7 +600,8 @@ const collectConflicts = ( // crane checkStringConflict('deploymentStrategy') // 'customHeaders' are keys only so duplicates are allowed - checkBooleanConflict('proxyHeaders') + checkBooleanConflict('proxyBuffering') + // 'proxyHeaders' are keys only so duplicates are allowed checkBooleanConflict('useLoadBalancer') checkUniqueKeyValuesConflict('extraLBAnnotations') checkObjectConflict('healthCheckConfig') diff --git a/web/crux-ui/src/models/container-errors.ts b/web/crux-ui/src/models/container-errors.ts index 6fe17da37..3ea6d9fd8 100644 --- a/web/crux-ui/src/models/container-errors.ts +++ b/web/crux-ui/src/models/container-errors.ts @@ -57,7 +57,7 @@ export type ContainerConfigErrors = { // crane deploymentStrategy?: string - proxyHeaders?: string + proxyBuffering?: string useLoadBalancer?: string extraLBAnnotations?: UniqueItemErrors healthCheckConfig?: string @@ -177,7 +177,7 @@ export const conflictsToError = ( // crane checkStringError('deploymentStrategy') - checkStringError('proxyHeaders') + checkStringError('proxyBuffering') checkStringError('useLoadBalancer') checkUniqueErrors('extraLBAnnotations') checkStringError('healthCheckConfig') diff --git a/web/crux-ui/src/models/container-merge.ts b/web/crux-ui/src/models/container-merge.ts index 514b73fdf..d098ba899 100644 --- a/web/crux-ui/src/models/container-merge.ts +++ b/web/crux-ui/src/models/container-merge.ts @@ -174,8 +174,9 @@ const squashConfigs = (strong: ContainerConfigData, weak: ContainerConfigData): storage: strong.storage ?? weak.storage, // crane - customHeaders: strong.customHeaders ?? weak.customHeaders, - proxyHeaders: mergeBoolean(strong.proxyHeaders, weak.proxyHeaders), + corsHeaders: strong.corsHeaders ?? weak.corsHeaders, + proxyBuffering: mergeBoolean(strong.proxyBuffering, weak.proxyBuffering), + proxyHeaders: strong.proxyHeaders ?? weak.proxyHeaders, extraLBAnnotations: strong.extraLBAnnotations ?? weak.extraLBAnnotations, healthCheckConfig: strong.healthCheckConfig ?? weak.healthCheckConfig, resourceConfig: strong.resourceConfig ?? weak.resourceConfig, @@ -214,8 +215,9 @@ const mergeConfigs = (strong: ContainerConfigData, weak: ContainerConfigData): C storage: strong.storage ?? weak.storage, // crane - customHeaders: mergeUniqueKeys(strong.customHeaders, weak.customHeaders), - proxyHeaders: mergeBoolean(strong.proxyHeaders, weak.proxyHeaders), + corsHeaders: mergeUniqueKeys(strong.corsHeaders, weak.corsHeaders), + proxyBuffering: mergeBoolean(strong.proxyBuffering, weak.proxyBuffering), + proxyHeaders: mergeUniqueKeys(strong.proxyHeaders, weak.proxyHeaders), extraLBAnnotations: mergeUniqueKeyValues(strong.extraLBAnnotations, weak.extraLBAnnotations), healthCheckConfig: strong.healthCheckConfig ?? weak.healthCheckConfig ?? null, resourceConfig: strong.resourceConfig ?? weak.resourceConfig ?? null, @@ -285,8 +287,9 @@ export const mergeInstanceConfigWithDeploymentConfig = ( storage: instance.storage ?? deployment.storage, // crane - customHeaders: mergeUniqueKeys(instance.customHeaders, deployment.customHeaders), - proxyHeaders: mergeBoolean(instance.proxyHeaders, deployment.proxyHeaders), + corsHeaders: mergeUniqueKeys(instance.corsHeaders, deployment.corsHeaders), + proxyBuffering: mergeBoolean(instance.proxyBuffering, deployment.proxyBuffering), + proxyHeaders: mergeUniqueKeys(instance.proxyHeaders, deployment.proxyHeaders), extraLBAnnotations: mergeUniqueKeyValues(instance.extraLBAnnotations, deployment.extraLBAnnotations), healthCheckConfig: instance.healthCheckConfig ?? deployment.healthCheckConfig ?? null, resourceConfig: instance.resourceConfig ?? deployment.resourceConfig ?? null, diff --git a/web/crux-ui/src/models/container.ts b/web/crux-ui/src/models/container.ts index 484e0b5e5..380353ec9 100644 --- a/web/crux-ui/src/models/container.ts +++ b/web/crux-ui/src/models/container.ts @@ -251,8 +251,9 @@ export type ContainerConfigData = { // crane deploymentStrategy?: ContainerDeploymentStrategyType - customHeaders?: UniqueKey[] - proxyHeaders?: boolean + corsHeaders?: UniqueKey[] + proxyBuffering?: boolean + proxyHeaders?: UniqueKey[] useLoadBalancer?: boolean extraLBAnnotations?: UniqueKeyValue[] healthCheckConfig?: ContainerConfigHealthCheck @@ -284,12 +285,13 @@ export const COMMON_CONFIG_KEYS = [ export const CRANE_CONFIG_KEYS = [ 'annotations', - 'customHeaders', + 'corsHeaders', 'deploymentStrategy', 'extraLBAnnotations', 'healthCheckConfig', 'labels', 'metrics', + 'proxyBuffering', 'proxyHeaders', 'resourceConfig', 'useLoadBalancer', diff --git a/web/crux-ui/src/models/json-container.ts b/web/crux-ui/src/models/json-container.ts index 44e2386ee..fa0e6982b 100644 --- a/web/crux-ui/src/models/json-container.ts +++ b/web/crux-ui/src/models/json-container.ts @@ -102,8 +102,9 @@ export type JsonContainerConfig = { // crane deploymentStrategy?: ContainerDeploymentStrategyType - customHeaders?: string[] - proxyHeaders?: boolean + corsHeaders?: string[] + proxyBuffering?: boolean + proxyHeaders?: string[] useLoadBalancer?: boolean extraLBAnnotations?: JsonKeyValue healthCheckConfig?: JsonHealthCheck @@ -212,8 +213,9 @@ export const containerConfigToJsonConfig = (config: ContainerConfigData): JsonCo // crane deploymentStrategy: config.deploymentStrategy ?? null, - customHeaders: keyArrayToJson(config.customHeaders), - proxyHeaders: booleanToJson(config.proxyHeaders), + corsHeaders: keyArrayToJson(config.corsHeaders), + proxyBuffering: booleanToJson(config.proxyBuffering), + proxyHeaders: keyArrayToJson(config.proxyHeaders), useLoadBalancer: booleanToJson(config.useLoadBalancer), extraLBAnnotations: keyValueArrayToJson(config.extraLBAnnotations), healthCheckConfig: healthCheckConfigToJson(config.healthCheckConfig), @@ -455,7 +457,8 @@ export const mergeJsonConfigToConcreteContainerConfig = ( replicas: json.replicas ?? config.replicas, name: json.name ?? config.name, networkMode: json.networkMode ?? config.networkMode, - proxyHeaders: json.proxyHeaders ?? config.proxyHeaders, + proxyBuffering: mergeBoolean(json.proxyBuffering, config.proxyBuffering), + proxyHeaders: mergeKeysWithJson(config.proxyHeaders, json.proxyHeaders), resourceConfig: json.resourceConfig ?? config.resourceConfig, restartPolicy: json.restartPolicy ?? config.restartPolicy, routing: json.routing ?? config.routing, @@ -469,7 +472,7 @@ export const mergeJsonConfigToConcreteContainerConfig = ( extraLBAnnotations: mergeKeyValuesWithJson(config.extraLBAnnotations, json.extraLBAnnotations), capabilities: mergeKeyValuesWithJson(config.capabilities, json.capabilities), commands: mergeKeysWithJson(config.commands, json.commands), - customHeaders: mergeKeysWithJson(config.customHeaders, json.customHeaders), + corsHeaders: mergeKeysWithJson(config.corsHeaders, json.corsHeaders), networks: mergeKeysWithJson(config.networks, json.networks), args: mergeKeysWithJson(config.args, json.args), logConfig: json.logConfig diff --git a/web/crux-ui/src/validations/container.ts b/web/crux-ui/src/validations/container.ts index 5f9993dcd..ec34c1dac 100644 --- a/web/crux-ui/src/validations/container.ts +++ b/web/crux-ui/src/validations/container.ts @@ -603,8 +603,9 @@ const createContainerConfigBaseSchema = (imageLabels: Record) => // crane deploymentStrategy: deploymentStrategyRule, - customHeaders: uniqueKeysOnlySchema.default(null).nullable().optional().label('container:crane.customHeaders'), - proxyHeaders: yup.boolean().default(null).nullable().optional().label('container:crane.proxyHeaders'), + corsHeaders: uniqueKeysOnlySchema.default(null).nullable().optional().label('container:crane.corsHeaders'), + proxyBuffering: yup.boolean().default(null).nullable().optional().label('container:crane.proxyBuffering'), + proxyHeaders: uniqueKeysOnlySchema.default(null).nullable().optional().label('container:crane.proxyHeaders'), useLoadBalancer: yup.boolean().default(null).nullable().optional().label('container:crane.useLoadBalancer'), extraLBAnnotations: uniqueKeyValuesSchema .default(null) diff --git a/web/crux/prisma/migrations/20250923153744_proxy_headers_rework/migration.sql b/web/crux/prisma/migrations/20250923153744_proxy_headers_rework/migration.sql new file mode 100644 index 000000000..58dd8d349 --- /dev/null +++ b/web/crux/prisma/migrations/20250923153744_proxy_headers_rework/migration.sql @@ -0,0 +1,3 @@ +ALTER TABLE "ContainerConfig" RENAME "proxyHeaders" TO "proxyBuffering"; +ALTER TABLE "ContainerConfig" RENAME "customHeaders" TO "corsHeaders"; +ALTER TABLE "ContainerConfig" ADD COLUMN "proxyHeaders" JSONB; diff --git a/web/crux/prisma/schema.prisma b/web/crux/prisma/schema.prisma index 76265fbc8..dc42d96b3 100644 --- a/web/crux/prisma/schema.prisma +++ b/web/crux/prisma/schema.prisma @@ -296,14 +296,15 @@ model ContainerConfig { deploymentStrategy DeploymentStrategy? healthCheckConfig Json? resourceConfig Json? - proxyHeaders Boolean? + proxyBuffering Boolean? + proxyHeaders Json? useLoadBalancer Boolean? extraLBAnnotations Json? - customHeaders Json? + corsHeaders Json? annotations Json? labels Json? metrics Json? - replicas Int? + replicas Int? image Image? instance Instance? @@ -315,13 +316,13 @@ model ContainerConfig { } model Deployment { - id String @id @default(uuid()) @db.Uuid - createdAt DateTime @default(now()) @db.Timestamptz(6) - createdBy String @db.Uuid - updatedAt DateTime @updatedAt @db.Timestamptz(6) - updatedBy String? @db.Uuid + id String @id @default(uuid()) @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + createdBy String @db.Uuid + updatedAt DateTime @updatedAt @db.Timestamptz(6) + updatedBy String? @db.Uuid deployedAt DateTime? @db.Timestamptz(6) - deployedBy String? @db.Uuid + deployedBy String? @db.Uuid note String? prefix String? @@ -654,7 +655,7 @@ model ConfigBundle { teamId String @db.Uuid config ContainerConfig? @relation(fields: [configId], references: [id], onDelete: Cascade) - configId String @unique @db.Uuid + configId String @unique @db.Uuid deployments ConfigBundleOnDeployments[] diff --git a/web/crux/src/app/container/container.const.ts b/web/crux/src/app/container/container.const.ts index 633ac8dd7..8a09e1c99 100644 --- a/web/crux/src/app/container/container.const.ts +++ b/web/crux/src/app/container/container.const.ts @@ -18,7 +18,8 @@ export const COMMON_CONFIG_PROPERTIES = [ export const CRANE_CONFIG_PROPERTIES = [ 'deploymentStrategy', - 'customHeaders', + 'corsHeaders', + 'proxyBuffering', 'proxyHeaders', 'useLoadBalancer', 'extraLBAnnotations', diff --git a/web/crux/src/app/container/container.dto.ts b/web/crux/src/app/container/container.dto.ts index ef947d755..a77b22eea 100644 --- a/web/crux/src/app/container/container.dto.ts +++ b/web/crux/src/app/container/container.dto.ts @@ -454,11 +454,15 @@ export class ContainerConfigDto { @IsOptional() @ValidateNested({ each: true }) - customHeaders?: UniqueKeyDto[] + corsHeaders?: UniqueKeyDto[] @IsBoolean() @IsOptional() - proxyHeaders?: boolean + proxyBuffering?: boolean + + @IsOptional() + @ValidateNested({ each: true }) + proxyHeaders?: UniqueKeyDto[] @IsBoolean() @IsOptional() diff --git a/web/crux/src/app/container/container.mapper.ts b/web/crux/src/app/container/container.mapper.ts index a96e3b383..e0d8e5351 100644 --- a/web/crux/src/app/container/container.mapper.ts +++ b/web/crux/src/app/container/container.mapper.ts @@ -224,9 +224,10 @@ export default class ContainerMapper { deploymentStrategy: config.deploymentStrategy ?? null, healthCheckConfig: config.healthCheckConfig ?? null, resourceConfig: config.resourceConfig ?? null, - proxyHeaders: toNullableBoolean(config.proxyHeaders), + proxyBuffering: toNullableBoolean(config.proxyBuffering), + proxyHeaders: config.proxyHeaders ?? null, useLoadBalancer: toNullableBoolean(config.useLoadBalancer), - customHeaders: config.customHeaders ?? null, + corsHeaders: config.corsHeaders ?? null, extraLBAnnotations: config.extraLBAnnotations ?? null, capabilities: config.capabilities ?? null, annotations: config.annotations ?? null, @@ -273,9 +274,10 @@ export default class ContainerMapper { deploymentStrategy: config.deploymentStrategy ?? null, healthCheckConfig: toPrismaJson(config.healthCheckConfig), resourceConfig: toPrismaJson(config.resourceConfig), - proxyHeaders: toNullableBoolean(config.proxyHeaders), + proxyBuffering: toNullableBoolean(config.proxyBuffering), + proxyHeaders: toPrismaJsonArray(config.proxyHeaders), useLoadBalancer: toNullableBoolean(config.useLoadBalancer), - customHeaders: toPrismaJsonArray(config.customHeaders), + corsHeaders: toPrismaJsonArray(config.corsHeaders), extraLBAnnotations: toPrismaJsonArray(config.extraLBAnnotations), capabilities: toPrismaJsonArray(config.capabilities), annotations: toPrismaJson(config.annotations), @@ -318,9 +320,10 @@ export default class ContainerMapper { deploymentStrategy: 'deploymentStrategy' in config ? (config.deploymentStrategy ?? null) : undefined, healthCheckConfig: 'healthCheckConfig' in config ? toPrismaJson(config.healthCheckConfig) : undefined, resourceConfig: 'resourceConfig' in config ? toPrismaJson(config.resourceConfig) : undefined, - proxyHeaders: 'proxyHeaders' in config ? toNullableBoolean(config.proxyHeaders) : undefined, + proxyBuffering: 'proxyBuffering' in config ? toNullableBoolean(config.proxyBuffering) : undefined, + proxyHeaders: 'proxyHeaders' in config ? toPrismaJsonArray(config.proxyHeaders) : undefined, useLoadBalancer: 'useLoadBalancer' in config ? toNullableBoolean(config.useLoadBalancer) : undefined, - customHeaders: 'customHeaders' in config ? toPrismaJsonArray(config.customHeaders) : undefined, + corsHeaders: 'corsHeaders' in config ? toPrismaJsonArray(config.corsHeaders) : undefined, extraLBAnnotations: 'extraLBAnnotations' in config ? toPrismaJsonArray(config.extraLBAnnotations) : undefined, capabilities: 'capabilities' in config ? toPrismaJsonArray(config.capabilities) : undefined, annotations: 'annotations' in config ? toPrismaJson(config.annotations) : undefined, diff --git a/web/crux/src/app/deploy/deploy.mapper.spec.ts b/web/crux/src/app/deploy/deploy.mapper.spec.ts index 56105a425..cb560fc5e 100644 --- a/web/crux/src/app/deploy/deploy.mapper.spec.ts +++ b/web/crux/src/app/deploy/deploy.mapper.spec.ts @@ -56,7 +56,8 @@ describe('DeployMapper', () => { deploymentStrategy: 'recreate', expose: 'expose', networkMode: 'bridge', - proxyHeaders: false, + proxyBuffering: false, + proxyHeaders: [{ id: 'proxyHeaders', key: 'proxyHeaders' }], restartPolicy: 'no', tty: false, useLoadBalancer: false, @@ -124,10 +125,10 @@ describe('DeployMapper', () => { path: 'configCont', volume: 'configCont', }, - customHeaders: [ + corsHeaders: [ { - id: 'customHead', - key: 'customHead', + id: 'corsHeaders', + key: 'corsHeaders', }, ], dockerLabels: [ @@ -290,7 +291,13 @@ describe('DeployMapper', () => { deploymentStrategy: 'recreate', expose: 'exposeWithTls', networkMode: 'host', - proxyHeaders: true, + proxyBuffering: true, + proxyHeaders: [ + { + id: 'instance.proxyHeaders', + key: 'instance.proxyHeaders', + }, + ], restartPolicy: 'onFailure', tty: true, useLoadBalancer: true, @@ -358,10 +365,10 @@ describe('DeployMapper', () => { path: 'instance.configCont', volume: 'instance.configCont', }, - customHeaders: [ + corsHeaders: [ { - id: 'instance.customHead', - key: 'instance.customHead', + id: 'instance.corsHeaders', + key: 'instance.corsHeaders', }, ], dockerLabels: [ diff --git a/web/crux/src/app/deploy/deploy.mapper.ts b/web/crux/src/app/deploy/deploy.mapper.ts index ba29bd1c7..b3059598e 100644 --- a/web/crux/src/app/deploy/deploy.mapper.ts +++ b/web/crux/src/app/deploy/deploy.mapper.ts @@ -453,12 +453,13 @@ export default class DeployMapper { craneConfigToAgentProto(config: ConcreteContainerConfigData): CraneContainerConfig { return { - customHeaders: this.mapUniqueKeyToStringArray(config.customHeaders), + corsHeaders: this.mapUniqueKeyToStringArray(config.corsHeaders), extraLBAnnotations: this.mapKeyValueToMap(config.extraLBAnnotations), deploymentStrategy: this.deploymentStrategyToProto(config.deploymentStrategy) ?? ProtoDeploymentStrategy.ROLLING_UPDATE, healthCheckConfig: this.healthCheckToProto(config.healthCheckConfig), - proxyHeaders: config.proxyHeaders, + proxyBuffering: config.proxyBuffering, + proxyHeaders: this.mapUniqueKeyToStringArray(config.proxyHeaders), useLoadBalancer: config.useLoadBalancer, resourceConfig: { limits: config.resourceConfig?.limits, diff --git a/web/crux/src/domain/container-conflict.ts b/web/crux/src/domain/container-conflict.ts index 2ec5d0601..253aa8b9b 100644 --- a/web/crux/src/domain/container-conflict.ts +++ b/web/crux/src/domain/container-conflict.ts @@ -605,8 +605,9 @@ const collectConflicts = ( // crane checkStringConflict('deploymentStrategy') - // 'customHeaders' are keys only so duplicates are allowed - checkBooleanConflict('proxyHeaders') + // 'corsHeaders' are keys only so duplicates are allowed + checkBooleanConflict('proxyBuffering') + // 'proxyHeaders' are keys only so duplicates are allowed checkBooleanConflict('useLoadBalancer') checkUniqueKeyValuesConflict('extraLBAnnotations') checkObjectConflict('healthCheckConfig') diff --git a/web/crux/src/domain/container-merge.spec.ts b/web/crux/src/domain/container-merge.spec.ts index 4e28d0f52..cd9167fbb 100644 --- a/web/crux/src/domain/container-merge.spec.ts +++ b/web/crux/src/domain/container-merge.spec.ts @@ -9,7 +9,8 @@ describe('container-merge', () => { workingDirectory: '/app', expose: 'expose', networkMode: 'bridge', - proxyHeaders: false, + proxyBuffering: false, + proxyHeaders: [{ id: 'proxyHeaders', key: 'proxyHeaders' }], restartPolicy: 'no', tty: false, useLoadBalancer: false, @@ -77,10 +78,10 @@ describe('container-merge', () => { path: 'configCont', volume: 'configCont', }, - customHeaders: [ + corsHeaders: [ { - id: 'customHead', - key: 'customHead', + id: 'corsHeaders', + key: 'corsHeaders', }, ], dockerLabels: [ @@ -249,7 +250,8 @@ describe('container-merge', () => { workingDirectory: '/app', expose: 'exposeWithTls', networkMode: 'host', - proxyHeaders: true, + proxyBuffering: true, + proxyHeaders: [{ id: 'concrete.proxyHeaders', key: 'concrete.proxyHeaders' }], restartPolicy: 'onFailure', tty: true, useLoadBalancer: true, @@ -317,10 +319,10 @@ describe('container-merge', () => { path: 'concrete.configCont', volume: 'concrete.configCont', }, - customHeaders: [ + corsHeaders: [ { - id: 'concrete.customHead', - key: 'concrete.customHead', + id: 'concrete.corsHeaders', + key: 'concrete.corsHeaders', }, ], dockerLabels: [ @@ -487,7 +489,8 @@ describe('container-merge', () => { workingDirectory: '/app', expose: 'exposeWithTls', networkMode: 'host', - proxyHeaders: true, + proxyBuffering: true, + proxyHeaders: [{ id: 'deployment.proxyHeaders', key: 'deployment.proxyHeaders' }], restartPolicy: 'onFailure', tty: true, useLoadBalancer: true, @@ -555,10 +558,10 @@ describe('container-merge', () => { path: 'deployment.configCont', volume: 'deployment.configCont', }, - customHeaders: [ + corsHeaders: [ { - id: 'deployment.customHead', - key: 'deployment.customHead', + id: 'deployment.corsHeaders', + key: 'deployment.corsHeaders', }, ], dockerLabels: [ @@ -858,7 +861,8 @@ describe('container-merge', () => { ...fullConcreteConfig, args: [...instance.args, ...deployment.args], commands: [...instance.commands, ...deployment.commands], - customHeaders: [...instance.customHeaders, ...deployment.customHeaders], + corsHeaders: [...instance.corsHeaders, ...deployment.corsHeaders], + proxyHeaders: [...instance.proxyHeaders, ...deployment.proxyHeaders], dockerLabels: [...instance.dockerLabels, ...deployment.dockerLabels], environment: [...instance.environment, ...deployment.environment], extraLBAnnotations: [...instance.extraLBAnnotations, ...deployment.extraLBAnnotations], diff --git a/web/crux/src/domain/container-merge.ts b/web/crux/src/domain/container-merge.ts index aac72a6b8..185104e78 100644 --- a/web/crux/src/domain/container-merge.ts +++ b/web/crux/src/domain/container-merge.ts @@ -206,8 +206,9 @@ const squashConfigs = (strong: ContainerConfigData, weak: ContainerConfigData): ...mergeStorage(strong, weak), // crane - customHeaders: strong.customHeaders ?? weak.customHeaders, - proxyHeaders: mergeBoolean(strong.proxyHeaders, weak.proxyHeaders), + corsHeaders: strong.corsHeaders ?? weak.corsHeaders, + proxyBuffering: mergeBoolean(strong.proxyBuffering, weak.proxyBuffering), + proxyHeaders: strong.proxyHeaders ?? weak.proxyHeaders, extraLBAnnotations: strong.extraLBAnnotations ?? weak.extraLBAnnotations, healthCheckConfig: strong.healthCheckConfig ?? weak.healthCheckConfig, resourceConfig: strong.resourceConfig ?? weak.resourceConfig, @@ -246,8 +247,9 @@ const mergeConfigs = (strong: ContainerConfigData, weak: ContainerConfigData): C ...mergeStorage(strong, weak), // crane - customHeaders: mergeUniqueKeys(strong.customHeaders, weak.customHeaders), - proxyHeaders: mergeBoolean(strong.proxyHeaders, weak.proxyHeaders), + corsHeaders: mergeUniqueKeys(strong.corsHeaders, weak.corsHeaders), + proxyBuffering: mergeBoolean(strong.proxyBuffering, weak.proxyBuffering), + proxyHeaders: mergeUniqueKeys(strong.proxyHeaders, weak.proxyHeaders), extraLBAnnotations: mergeUniqueKeyValues(strong.extraLBAnnotations, weak.extraLBAnnotations), healthCheckConfig: strong.healthCheckConfig ?? weak.healthCheckConfig ?? null, resourceConfig: strong.resourceConfig ?? weak.resourceConfig ?? null, @@ -317,8 +319,9 @@ export const mergeInstanceConfigWithDeploymentConfig = ( ...mergeStorage(instance, deployment), // crane - customHeaders: mergeUniqueKeys(instance.customHeaders, deployment.customHeaders), - proxyHeaders: mergeBoolean(instance.proxyHeaders, deployment.proxyHeaders), + corsHeaders: mergeUniqueKeys(instance.corsHeaders, deployment.corsHeaders), + proxyBuffering: mergeBoolean(instance.proxyBuffering, deployment.proxyBuffering), + proxyHeaders: mergeUniqueKeys(instance.proxyHeaders, deployment.proxyHeaders), extraLBAnnotations: mergeUniqueKeyValues(instance.extraLBAnnotations, deployment.extraLBAnnotations), healthCheckConfig: instance.healthCheckConfig ?? deployment.healthCheckConfig ?? null, resourceConfig: instance.resourceConfig ?? deployment.resourceConfig ?? null, diff --git a/web/crux/src/domain/container.ts b/web/crux/src/domain/container.ts index c2f028aa8..f3e518ce6 100644 --- a/web/crux/src/domain/container.ts +++ b/web/crux/src/domain/container.ts @@ -210,8 +210,9 @@ export type ContainerConfigData = { // crane deploymentStrategy?: ContainerDeploymentStrategyType - customHeaders?: UniqueKey[] - proxyHeaders?: boolean + corsHeaders?: UniqueKey[] + proxyBuffering?: boolean + proxyHeaders?: UniqueKey[] useLoadBalancer?: boolean extraLBAnnotations?: UniqueKeyValue[] healthCheckConfig?: HealthCheck @@ -229,7 +230,8 @@ export type ContainerConfigDataWithId = ContainerConfigData & { type DagentSpecificConfigKeys = 'logConfig' | 'restartPolicy' | 'networkMode' | 'networks' | 'dockerLabels' type CraneSpecificConfigKeys = | 'deploymentStrategy' - | 'customHeaders' + | 'corsHeaders' + | 'proxyBuffering' | 'proxyHeaders' | 'useLoadBalancer' | 'extraLBAnnotations' diff --git a/web/crux/src/domain/validation.ts b/web/crux/src/domain/validation.ts index 4ed2a3bb5..c39685aba 100644 --- a/web/crux/src/domain/validation.ts +++ b/web/crux/src/domain/validation.ts @@ -314,8 +314,9 @@ export const concreteContainerConfigSchema = yup.object().shape({ // crane deploymentStrategy: deploymentStrategyRule.optional().nullable(), - customHeaders: uniqueKeysOnlySchema.optional().nullable(), - proxyHeaders: yup.boolean().optional().nullable(), + corsHeaders: uniqueKeysOnlySchema.optional().nullable(), + proxyBuffering: yup.boolean().optional().nullable(), + proxyHeaders: uniqueKeysOnlySchema.optional().nullable(), useLoadBalancer: yup.boolean().optional().nullable(), extraLBAnnotations: uniqueKeyValuesSchema.optional().nullable(), healthCheckConfig: healthCheckConfigRule.optional().nullable(), From 3eff9d233f5458404baf4fabef23bd0b373d2822 Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Wed, 24 Sep 2025 15:48:45 +0200 Subject: [PATCH 4/5] fix: e2e --- .../kubernetes-editor.spec.ts | 40 +++++++++++++++---- .../container-config/kubernetes-json.spec.ts | 4 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts b/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts index 4170cb045..2daed194d 100644 --- a/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts +++ b/web/crux-ui/e2e/with-login/container-config/kubernetes-editor.spec.ts @@ -11,6 +11,7 @@ import { wsPatchMatchIngressLabel, wsPatchMatchLBAnnotations, wsPatchMatchLoadBalancer, + wsPatchMatchProxyBuffering, wsPatchMatchProxyHeader, wsPatchMatchReplicas, wsPatchMatchResourceConfig, @@ -56,8 +57,8 @@ test.describe('Image kubernetes config from editor', () => { await expect(page.locator(`button.bg-dyo-turquoise:has-text("${strategy}")`)).toBeVisible() }) - test('Custom headers should be saved', async ({ page }) => { - const { imageConfigId } = await setup(page, 'custom-headers-editor', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) + test('CORS headers should be saved', async ({ page }) => { + const { imageConfigId } = await setup(page, 'cors-headers-editor', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) const sock = waitSocketRef(page) await page.goto(TEAM_ROUTES.containerConfig.details(imageConfigId)) @@ -65,10 +66,10 @@ test.describe('Image kubernetes config from editor', () => { const ws = await sock const wsRoute = TEAM_ROUTES.containerConfig.detailsSocket(imageConfigId) - await page.locator('button:has-text("Custom headers")').click() + await page.locator('button:has-text("CORS headers")').click() const header = 'test-header' - const input = page.locator('div:has(label:has-text("CUSTOM HEADERS")) input[placeholder="Header name"]').first() + const input = page.locator('div:has(label:has-text("CORS HEADERS")) input[placeholder="Header name"]').first() const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchCorsHeader(header)) await input.fill(header) @@ -79,6 +80,28 @@ test.describe('Image kubernetes config from editor', () => { await expect(input).toHaveValue(header) }) + test('Proxy buffering should be saved', async ({ page }) => { + const { imageConfigId } = await setup(page, 'proxy-buffering-editor', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) + + const sock = waitSocketRef(page) + await page.goto(TEAM_ROUTES.containerConfig.details(imageConfigId)) + await page.waitForSelector('h2:text-is("Image config")') + const ws = await sock + const wsRoute = TEAM_ROUTES.containerConfig.detailsSocket(imageConfigId) + + await page.locator('button:has-text("Proxy buffering")').click() + + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyBuffering(true)) + await page.locator('button[aria-checked="false"]:right-of(label:has-text("PROXY BUFFERING"))').click() + await wsSent + + await page.reload() + + await expect( + page.locator(':right-of(:text("PROXY BUFFERING"))').getByRole('switch', { checked: true }), + ).toBeVisible() + }) + test('Proxy headers should be saved', async ({ page }) => { const { imageConfigId } = await setup(page, 'proxy-headers-editor', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) @@ -90,13 +113,16 @@ test.describe('Image kubernetes config from editor', () => { await page.locator('button:has-text("Proxy headers")').click() - const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyHeader(true)) - await page.locator('button[aria-checked="false"]:right-of(label:has-text("PROXY HEADERS"))').click() + const header = 'test-header' + const input = page.locator('div:has(label:has-text("PROXY HEADERS")) input[placeholder="Header name"]').first() + + const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyHeader(header)) + await input.fill(header) await wsSent await page.reload() - await expect(page.locator(':right-of(:text("PROXY HEADERS"))').getByRole('switch', { checked: true })).toBeVisible() + await expect(input).toHaveValue(header) }) test('Load balancer should be saved', async ({ page }) => { diff --git a/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts b/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts index 0aa1afdcc..6a3ea3862 100644 --- a/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts +++ b/web/crux-ui/e2e/with-login/container-config/kubernetes-json.spec.ts @@ -63,7 +63,7 @@ test.describe('Image kubernetes config from JSON', () => { await expect(page.locator(`button.bg-dyo-turquoise:has-text("${strategy}")`)).toBeVisible() }) - test('Cors headers should be saved', async ({ page }) => { + test('CORS headers should be saved', async ({ page }) => { const { imageConfigId } = await setup(page, 'cors-headers-json', '1.0.0', NGINX_TEST_IMAGE_WITH_TAG) const sock = waitSocketRef(page) @@ -134,7 +134,7 @@ test.describe('Image kubernetes config from JSON', () => { const jsonEditor = await page.locator('textarea') const json = JSON.parse(await jsonEditor.inputValue()) - json.customHeaders = [header] + json.proxyHeaders = [header] const wsSent = wsPatchSent(ws, wsRoute, WS_TYPE_PATCH_CONFIG, wsPatchMatchProxyHeader(header)) await jsonEditor.fill(JSON.stringify(json)) From ea5d727b6b727dcbf74c917a6029a076a756576a Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Thu, 30 Oct 2025 14:27:02 +0100 Subject: [PATCH 5/5] fix crane tls config ignored --- golang/pkg/crane/k8s/ingress.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/golang/pkg/crane/k8s/ingress.go b/golang/pkg/crane/k8s/ingress.go index 150719efd..0ae67d780 100644 --- a/golang/pkg/crane/k8s/ingress.go +++ b/golang/pkg/crane/k8s/ingress.go @@ -47,7 +47,6 @@ type DeployIngressOptions struct { name string namespace string routing routingOptions - tls bool } func newIngress(ctx context.Context, client *Client) *ingress { @@ -112,7 +111,7 @@ func (ing *ingress) deployIngress(options *DeployIngressOptions) error { ), ), ))) - tlsConf := getTLSConfig(ingressDomain, options.containerName, options.tls) + tlsConf := getTLSConfig(ingressDomain, options.containerName, options.routing.tls) if tlsConf != nil { spec.WithTLS(tlsConf) }