From 2f72c0379fd9721401e9a5576cca3d1fca5275d4 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Fri, 21 Apr 2017 09:53:43 -0700 Subject: [PATCH 01/12] Support templated secrets and configs Add the ability for secrets and configs to expand templates, which can reference the following fields: - The same set of fields available to ContainerSpec templates ({{.Service.Name}}, {{.Node.ID}}, etc) - Environment variables: {{Env "envvar"}} - Other secrets: {{Secret "sometarget"}} - Other configs: {{Config "sometarget"}} Secrets and configs can be accessed either by source (secret name) or target (file name). Templating of secrets/configs is optional, and is disabled by default because it's very possible for Go template syntax to conflict with the syntax of the payload. The only change that will be necessary in the executor is wrapping the DependencyGetter in NewTemplatedDependencyGetter before handing it off to the execution backend. Signed-off-by: Aaron Lehmann --- api/specs.pb.go | 323 ++++++++++++++-------- api/specs.proto | 15 + template/context.go | 170 +++++++++++- template/expand.go | 45 +++ template/getter.go | 100 +++++++ template/getter_test.go | 586 ++++++++++++++++++++++++++++++++++++++++ template/template.go | 8 +- 7 files changed, 1127 insertions(+), 120 deletions(-) create mode 100644 template/getter.go create mode 100644 template/getter_test.go diff --git a/api/specs.pb.go b/api/specs.pb.go index 39b0c0abf0..04ddfaed10 100644 --- a/api/specs.pb.go +++ b/api/specs.pb.go @@ -24,6 +24,29 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// Templating controls whether the payload of a config or secret is evaluated +// as a template. +type Templating int32 + +const ( + Templating_NONE Templating = 0 + Templating_GO_TEMPLATE Templating = 1 +) + +var Templating_name = map[int32]string{ + 0: "NONE", + 1: "GO_TEMPLATE", +} +var Templating_value = map[string]int32{ + "NONE": 0, + "GO_TEMPLATE": 1, +} + +func (x Templating) String() string { + return proto.EnumName(Templating_name, int32(x)) +} +func (Templating) EnumDescriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0} } + type NodeSpec_Membership int32 const ( @@ -760,6 +783,9 @@ type SecretSpec struct { Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` // Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes) Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // Templating controls whether and how to evaluate the secret payload as + // a template. + Templating Templating `protobuf:"varint,3,opt,name=templating,proto3,enum=docker.swarmkit.v1.Templating" json:"templating,omitempty"` } func (m *SecretSpec) Reset() { *m = SecretSpec{} } @@ -773,6 +799,9 @@ type ConfigSpec struct { // TODO(aaronl): Do we want to revise this to include multiple payloads in a single // ConfigSpec? Define this to be a tar? etc... Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // Templating controls whether and how to evaluate the config payload as + // a template. + Templating Templating `protobuf:"varint,3,opt,name=templating,proto3,enum=docker.swarmkit.v1.Templating" json:"templating,omitempty"` } func (m *ConfigSpec) Reset() { *m = ConfigSpec{} } @@ -795,6 +824,7 @@ func init() { proto.RegisterType((*ClusterSpec)(nil), "docker.swarmkit.v1.ClusterSpec") proto.RegisterType((*SecretSpec)(nil), "docker.swarmkit.v1.SecretSpec") proto.RegisterType((*ConfigSpec)(nil), "docker.swarmkit.v1.ConfigSpec") + proto.RegisterEnum("docker.swarmkit.v1.Templating", Templating_name, Templating_value) proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Membership", NodeSpec_Membership_name, NodeSpec_Membership_value) proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Availability", NodeSpec_Availability_name, NodeSpec_Availability_value) proto.RegisterEnum("docker.swarmkit.v1.EndpointSpec_ResolutionMode", EndpointSpec_ResolutionMode_name, EndpointSpec_ResolutionMode_value) @@ -2227,6 +2257,11 @@ func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) i += copy(dAtA[i:], m.Data) } + if m.Templating != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.Templating)) + } return i, nil } @@ -2259,6 +2294,11 @@ func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) i += copy(dAtA[i:], m.Data) } + if m.Templating != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.Templating)) + } return i, nil } @@ -2685,6 +2725,9 @@ func (m *SecretSpec) Size() (n int) { if l > 0 { n += 1 + l + sovSpecs(uint64(l)) } + if m.Templating != 0 { + n += 1 + sovSpecs(uint64(m.Templating)) + } return n } @@ -2697,6 +2740,9 @@ func (m *ConfigSpec) Size() (n int) { if l > 0 { n += 1 + l + sovSpecs(uint64(l)) } + if m.Templating != 0 { + n += 1 + sovSpecs(uint64(m.Templating)) + } return n } @@ -2973,6 +3019,7 @@ func (this *SecretSpec) String() string { s := strings.Join([]string{`&SecretSpec{`, `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `Templating:` + fmt.Sprintf("%v", this.Templating) + `,`, `}`, }, "") return s @@ -2984,6 +3031,7 @@ func (this *ConfigSpec) String() string { s := strings.Join([]string{`&ConfigSpec{`, `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `Templating:` + fmt.Sprintf("%v", this.Templating) + `,`, `}`, }, "") return s @@ -5800,6 +5848,25 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { m.Data = []byte{} } iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType) + } + m.Templating = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Templating |= (Templating(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -5911,6 +5978,25 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { m.Data = []byte{} } iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType) + } + m.Templating = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Templating |= (Templating(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -6040,121 +6126,124 @@ var ( func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) } var fileDescriptorSpecs = []byte{ - // 1846 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcf, 0x73, 0x1b, 0x49, - 0x15, 0xb6, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xd2, 0x24, 0x61, 0xac, 0xb0, 0xb2, 0xa2, 0x0d, - 0xc1, 0xcb, 0x16, 0x72, 0x61, 0xa8, 0x25, 0x4b, 0x58, 0x40, 0xb2, 0x84, 0x63, 0x8c, 0x1d, 0x55, - 0xdb, 0x1b, 0xc8, 0x49, 0xd5, 0x9e, 0x69, 0x4b, 0x53, 0x1e, 0x75, 0x0f, 0xdd, 0x3d, 0xda, 0xd2, - 0x8d, 0xe3, 0x56, 0xae, 0x9c, 0x5d, 0x1c, 0xf8, 0x67, 0x72, 0xa4, 0x38, 0x71, 0x72, 0xb1, 0xfe, - 0x17, 0xb8, 0x71, 0x81, 0xea, 0x9e, 0x1e, 0xfd, 0x48, 0xc6, 0x9b, 0x54, 0x11, 0x6e, 0xdd, 0xaf, - 0xbf, 0xef, 0x75, 0xf7, 0xeb, 0xaf, 0xfb, 0xbd, 0x06, 0x47, 0x46, 0xd4, 0x93, 0xad, 0x48, 0x70, - 0xc5, 0x11, 0xf2, 0xb9, 0x77, 0x41, 0x45, 0x4b, 0x7e, 0x45, 0xc4, 0xf8, 0x22, 0x50, 0xad, 0xc9, - 0x8f, 0x6b, 0x8e, 0x9a, 0x46, 0xd4, 0x02, 0x6a, 0x77, 0x87, 0x7c, 0xc8, 0x4d, 0x73, 0x47, 0xb7, - 0xac, 0xb5, 0x3e, 0xe4, 0x7c, 0x18, 0xd2, 0x1d, 0xd3, 0x3b, 0x8b, 0xcf, 0x77, 0xfc, 0x58, 0x10, - 0x15, 0x70, 0x66, 0xc7, 0x37, 0xdf, 0x1c, 0x27, 0x6c, 0x9a, 0x0c, 0x35, 0x2f, 0xf3, 0x50, 0x3a, - 0xe6, 0x3e, 0x3d, 0x89, 0xa8, 0x87, 0xf6, 0xc1, 0x21, 0x8c, 0x71, 0x65, 0xb8, 0xd2, 0xcd, 0x35, - 0x72, 0xdb, 0xce, 0xee, 0x56, 0xeb, 0xed, 0x45, 0xb5, 0xda, 0x73, 0x58, 0x27, 0xff, 0xfa, 0x6a, - 0x6b, 0x05, 0x2f, 0x32, 0xd1, 0xaf, 0xa0, 0xe2, 0x53, 0x19, 0x08, 0xea, 0x0f, 0x04, 0x0f, 0xa9, - 0xbb, 0xda, 0xc8, 0x6d, 0xdf, 0xda, 0xfd, 0x5e, 0x96, 0x27, 0x3d, 0x39, 0xe6, 0x21, 0xc5, 0x8e, - 0x65, 0xe8, 0x0e, 0xda, 0x07, 0x18, 0xd3, 0xf1, 0x19, 0x15, 0x72, 0x14, 0x44, 0xee, 0x9a, 0xa1, - 0xff, 0xe0, 0x26, 0xba, 0x5e, 0x7b, 0xeb, 0x68, 0x06, 0xc7, 0x0b, 0x54, 0x74, 0x04, 0x15, 0x32, - 0x21, 0x41, 0x48, 0xce, 0x82, 0x30, 0x50, 0x53, 0x37, 0x6f, 0x5c, 0x7d, 0xf2, 0xad, 0xae, 0xda, - 0x0b, 0x04, 0xbc, 0x44, 0x6f, 0xfa, 0x00, 0xf3, 0x89, 0xd0, 0x63, 0x28, 0xf6, 0x7b, 0xc7, 0xdd, - 0x83, 0xe3, 0xfd, 0xea, 0x4a, 0x6d, 0xf3, 0xd5, 0x65, 0xe3, 0x9e, 0xf6, 0x31, 0x07, 0xf4, 0x29, - 0xf3, 0x03, 0x36, 0x44, 0xdb, 0x50, 0x6a, 0xef, 0xed, 0xf5, 0xfa, 0xa7, 0xbd, 0x6e, 0x35, 0x57, - 0xab, 0xbd, 0xba, 0x6c, 0xdc, 0x5f, 0x06, 0xb6, 0x3d, 0x8f, 0x46, 0x8a, 0xfa, 0xb5, 0xfc, 0xd7, - 0x7f, 0xad, 0xaf, 0x34, 0xbf, 0xce, 0x41, 0x65, 0x71, 0x11, 0xe8, 0x31, 0x14, 0xda, 0x7b, 0xa7, - 0x07, 0x2f, 0x7a, 0xd5, 0x95, 0x39, 0x7d, 0x11, 0xd1, 0xf6, 0x54, 0x30, 0xa1, 0xe8, 0x11, 0xac, - 0xf7, 0xdb, 0x5f, 0x9e, 0xf4, 0xaa, 0xb9, 0xf9, 0x72, 0x16, 0x61, 0x7d, 0x12, 0x4b, 0x83, 0xea, - 0xe2, 0xf6, 0xc1, 0x71, 0x75, 0x35, 0x1b, 0xd5, 0x15, 0x24, 0x60, 0x76, 0x29, 0x7f, 0xc9, 0x83, - 0x73, 0x42, 0xc5, 0x24, 0xf0, 0x3e, 0xb0, 0x44, 0x3e, 0x83, 0xbc, 0x22, 0xf2, 0xc2, 0x48, 0xc3, - 0xc9, 0x96, 0xc6, 0x29, 0x91, 0x17, 0x7a, 0x52, 0x4b, 0x37, 0x78, 0xad, 0x0c, 0x41, 0xa3, 0x30, - 0xf0, 0x88, 0xa2, 0xbe, 0x51, 0x86, 0xb3, 0xfb, 0xfd, 0x2c, 0x36, 0x9e, 0xa1, 0xec, 0xfa, 0x9f, - 0xad, 0xe0, 0x05, 0x2a, 0x7a, 0x0a, 0x85, 0x61, 0xc8, 0xcf, 0x48, 0x68, 0x34, 0xe1, 0xec, 0x3e, - 0xcc, 0x72, 0xb2, 0x6f, 0x10, 0x73, 0x07, 0x96, 0x82, 0x9e, 0x40, 0x21, 0x8e, 0x7c, 0xa2, 0xa8, - 0x5b, 0x30, 0xe4, 0x46, 0x16, 0xf9, 0x4b, 0x83, 0xd8, 0xe3, 0xec, 0x3c, 0x18, 0x62, 0x8b, 0x47, - 0x87, 0x50, 0x62, 0x54, 0x7d, 0xc5, 0xc5, 0x85, 0x74, 0x8b, 0x8d, 0xb5, 0x6d, 0x67, 0xf7, 0xd3, - 0x4c, 0x31, 0x26, 0x98, 0xb6, 0x52, 0xc4, 0x1b, 0x8d, 0x29, 0x53, 0x89, 0x9b, 0xce, 0xaa, 0x9b, - 0xc3, 0x33, 0x07, 0xe8, 0x17, 0x50, 0xa2, 0xcc, 0x8f, 0x78, 0xc0, 0x94, 0x5b, 0xba, 0x79, 0x21, - 0x3d, 0x8b, 0xd1, 0xc1, 0xc4, 0x33, 0x86, 0x66, 0x0b, 0x1e, 0x86, 0x67, 0xc4, 0xbb, 0x70, 0xcb, - 0xef, 0xb9, 0x8d, 0x19, 0xa3, 0x53, 0x80, 0xfc, 0x98, 0xfb, 0xb4, 0xb9, 0x03, 0x77, 0xde, 0x0a, - 0x35, 0xaa, 0x41, 0xc9, 0x86, 0x3a, 0xd1, 0x48, 0x1e, 0xcf, 0xfa, 0xcd, 0xdb, 0xb0, 0xb1, 0x14, - 0xd6, 0xe6, 0xdf, 0xf3, 0x50, 0x4a, 0xcf, 0x1a, 0xb5, 0xa1, 0xec, 0x71, 0xa6, 0x48, 0xc0, 0xa8, - 0xb0, 0xf2, 0xca, 0x3c, 0x99, 0xbd, 0x14, 0xa4, 0x59, 0xcf, 0x56, 0xf0, 0x9c, 0x85, 0x7e, 0x03, - 0x65, 0x41, 0x25, 0x8f, 0x85, 0x47, 0xa5, 0xd5, 0xd7, 0x76, 0xb6, 0x42, 0x12, 0x10, 0xa6, 0x7f, - 0x8c, 0x03, 0x41, 0x75, 0x94, 0x25, 0x9e, 0x53, 0xd1, 0x53, 0x28, 0x0a, 0x2a, 0x15, 0x11, 0xea, - 0xdb, 0x24, 0x82, 0x13, 0x48, 0x9f, 0x87, 0x81, 0x37, 0xc5, 0x29, 0x03, 0x3d, 0x85, 0x72, 0x14, - 0x12, 0xcf, 0x78, 0x75, 0xd7, 0x0d, 0xfd, 0xa3, 0x2c, 0x7a, 0x3f, 0x05, 0xe1, 0x39, 0x1e, 0x7d, - 0x0e, 0x10, 0xf2, 0xe1, 0xc0, 0x17, 0xc1, 0x84, 0x0a, 0x2b, 0xb1, 0x5a, 0x16, 0xbb, 0x6b, 0x10, - 0xb8, 0x1c, 0xf2, 0x61, 0xd2, 0x44, 0xfb, 0xff, 0x93, 0xbe, 0x16, 0xb4, 0x75, 0x08, 0x40, 0x66, - 0xa3, 0x56, 0x5d, 0x9f, 0xbc, 0x97, 0x2b, 0x7b, 0x22, 0x0b, 0x74, 0xf4, 0x10, 0x2a, 0xe7, 0x5c, - 0x78, 0x74, 0x60, 0x6f, 0x4d, 0xd9, 0x68, 0xc2, 0x31, 0xb6, 0x44, 0x5f, 0xa8, 0x03, 0xc5, 0x21, - 0x65, 0x54, 0x04, 0x9e, 0x0b, 0x66, 0xb2, 0xc7, 0x99, 0x17, 0x32, 0x81, 0xe0, 0x98, 0xa9, 0x60, - 0x4c, 0xed, 0x4c, 0x29, 0xb1, 0x53, 0x86, 0xa2, 0x48, 0x46, 0x9a, 0x7f, 0x00, 0xf4, 0x36, 0x16, - 0x21, 0xc8, 0x5f, 0x04, 0xcc, 0x37, 0xc2, 0x2a, 0x63, 0xd3, 0x46, 0x2d, 0x28, 0x46, 0x64, 0x1a, - 0x72, 0xe2, 0x5b, 0xb1, 0xdc, 0x6d, 0x25, 0xf9, 0xb2, 0x95, 0xe6, 0xcb, 0x56, 0x9b, 0x4d, 0x71, - 0x0a, 0x6a, 0x1e, 0xc2, 0xbd, 0xcc, 0x2d, 0xa3, 0x5d, 0xa8, 0xcc, 0x44, 0x38, 0x08, 0xec, 0x24, - 0x9d, 0xdb, 0xd7, 0x57, 0x5b, 0xce, 0x4c, 0xad, 0x07, 0x5d, 0xec, 0xcc, 0x40, 0x07, 0x7e, 0xf3, - 0xcf, 0x65, 0xd8, 0x58, 0x92, 0x32, 0xba, 0x0b, 0xeb, 0xc1, 0x98, 0x0c, 0xa9, 0x5d, 0x63, 0xd2, - 0x41, 0x3d, 0x28, 0x84, 0xe4, 0x8c, 0x86, 0x5a, 0xd0, 0xfa, 0x50, 0x7f, 0xf4, 0xce, 0x3b, 0xd1, - 0xfa, 0x9d, 0xc1, 0xf7, 0x98, 0x12, 0x53, 0x6c, 0xc9, 0xc8, 0x85, 0xa2, 0xc7, 0xc7, 0x63, 0xc2, - 0xf4, 0xd3, 0xb9, 0xb6, 0x5d, 0xc6, 0x69, 0x57, 0x47, 0x86, 0x88, 0xa1, 0x74, 0xf3, 0xc6, 0x6c, - 0xda, 0xa8, 0x0a, 0x6b, 0x94, 0x4d, 0xdc, 0x75, 0x63, 0xd2, 0x4d, 0x6d, 0xf1, 0x83, 0x44, 0x91, - 0x65, 0xac, 0x9b, 0x9a, 0x17, 0x4b, 0x2a, 0xdc, 0x62, 0x12, 0x51, 0xdd, 0x46, 0x3f, 0x83, 0xc2, - 0x98, 0xc7, 0x4c, 0x49, 0xb7, 0x64, 0x16, 0xbb, 0x99, 0xb5, 0xd8, 0x23, 0x8d, 0xb0, 0x4f, 0xbb, - 0x85, 0xa3, 0x1e, 0xdc, 0x91, 0x8a, 0x47, 0x83, 0xa1, 0x20, 0x1e, 0x1d, 0x44, 0x54, 0x04, 0xdc, - 0xb7, 0x4f, 0xd3, 0xe6, 0x5b, 0x87, 0xd2, 0xb5, 0x45, 0x0e, 0xbe, 0xad, 0x39, 0xfb, 0x9a, 0xd2, - 0x37, 0x0c, 0xd4, 0x87, 0x4a, 0x14, 0x87, 0xe1, 0x80, 0x47, 0x49, 0x96, 0x4a, 0xf4, 0xf4, 0x1e, - 0x21, 0xeb, 0xc7, 0x61, 0xf8, 0x3c, 0x21, 0x61, 0x27, 0x9a, 0x77, 0xd0, 0x7d, 0x28, 0x0c, 0x05, - 0x8f, 0x23, 0xe9, 0x3a, 0x26, 0x18, 0xb6, 0x87, 0xbe, 0x80, 0xa2, 0xa4, 0x9e, 0xa0, 0x4a, 0xba, - 0x15, 0xb3, 0xd5, 0x8f, 0xb3, 0x26, 0x39, 0x31, 0x10, 0x4c, 0xcf, 0xa9, 0xa0, 0xcc, 0xa3, 0x38, - 0xe5, 0xa0, 0x4d, 0x58, 0x53, 0x6a, 0xea, 0x6e, 0x34, 0x72, 0xdb, 0xa5, 0x4e, 0xf1, 0xfa, 0x6a, - 0x6b, 0xed, 0xf4, 0xf4, 0x25, 0xd6, 0x36, 0xfd, 0x82, 0x8e, 0xb8, 0x54, 0x8c, 0x8c, 0xa9, 0x7b, - 0xcb, 0xc4, 0x76, 0xd6, 0x47, 0x2f, 0x01, 0x7c, 0x26, 0x07, 0x9e, 0xb9, 0xb2, 0xee, 0x6d, 0xb3, - 0xbb, 0x4f, 0xdf, 0xbd, 0xbb, 0xee, 0xf1, 0x89, 0xcd, 0x22, 0x1b, 0xd7, 0x57, 0x5b, 0xe5, 0x59, - 0x17, 0x97, 0x7d, 0x26, 0x93, 0x26, 0xea, 0x80, 0x33, 0xa2, 0x24, 0x54, 0x23, 0x6f, 0x44, 0xbd, - 0x0b, 0xb7, 0x7a, 0x73, 0x5a, 0x78, 0x66, 0x60, 0xd6, 0xc3, 0x22, 0x49, 0x2b, 0x58, 0x2f, 0x55, - 0xba, 0x77, 0x4c, 0xac, 0x92, 0x0e, 0xfa, 0x08, 0x80, 0x47, 0x94, 0x0d, 0xa4, 0xf2, 0x03, 0xe6, - 0x22, 0xbd, 0x65, 0x5c, 0xd6, 0x96, 0x13, 0x6d, 0x40, 0x0f, 0xf4, 0xa3, 0x4d, 0xfc, 0x01, 0x67, - 0xe1, 0xd4, 0xfd, 0x8e, 0x19, 0x2d, 0x69, 0xc3, 0x73, 0x16, 0x4e, 0xd1, 0x16, 0x38, 0x46, 0x17, - 0x32, 0x18, 0x32, 0x12, 0xba, 0x77, 0x4d, 0x3c, 0x40, 0x9b, 0x4e, 0x8c, 0x45, 0x9f, 0x43, 0x12, - 0x0d, 0xe9, 0xde, 0xbb, 0xf9, 0x1c, 0xec, 0x62, 0xe7, 0xe7, 0x60, 0x39, 0xe8, 0x97, 0x00, 0x91, - 0x08, 0x26, 0x41, 0x48, 0x87, 0x54, 0xba, 0xf7, 0xcd, 0xa6, 0xeb, 0x99, 0xaf, 0xf5, 0x0c, 0x85, - 0x17, 0x18, 0xb5, 0xcf, 0xc1, 0x59, 0xb8, 0x6d, 0xfa, 0x96, 0x5c, 0xd0, 0xa9, 0xbd, 0xc0, 0xba, - 0xa9, 0x43, 0x32, 0x21, 0x61, 0x9c, 0x54, 0xc2, 0x65, 0x9c, 0x74, 0x7e, 0xbe, 0xfa, 0x24, 0x57, - 0xdb, 0x05, 0x67, 0x41, 0x75, 0xe8, 0x63, 0xd8, 0x10, 0x74, 0x18, 0x48, 0x25, 0xa6, 0x03, 0x12, - 0xab, 0x91, 0xfb, 0x6b, 0x43, 0xa8, 0xa4, 0xc6, 0x76, 0xac, 0x46, 0xb5, 0x01, 0xcc, 0x0f, 0x0f, - 0x35, 0xc0, 0xd1, 0xa2, 0x90, 0x54, 0x4c, 0xa8, 0xd0, 0xd9, 0x56, 0xc7, 0x7c, 0xd1, 0xa4, 0xc5, - 0x2b, 0x29, 0x11, 0xde, 0xc8, 0xbc, 0x1d, 0x65, 0x6c, 0x7b, 0xfa, 0x31, 0x48, 0x6f, 0x88, 0x7d, - 0x0c, 0x6c, 0xb7, 0xf9, 0xaf, 0x1c, 0x54, 0x16, 0x8b, 0x06, 0xb4, 0x97, 0x24, 0x7b, 0xb3, 0xa5, - 0x5b, 0xbb, 0x3b, 0xef, 0x2a, 0x32, 0x4c, 0x6a, 0x0d, 0x63, 0xed, 0xec, 0x48, 0xd7, 0xf7, 0x86, - 0x8c, 0x7e, 0x0a, 0xeb, 0x11, 0x17, 0x2a, 0x7d, 0xc2, 0xb2, 0x03, 0xcc, 0x45, 0x9a, 0x8a, 0x12, - 0x70, 0x73, 0x04, 0xb7, 0x96, 0xbd, 0xa1, 0x47, 0xb0, 0xf6, 0xe2, 0xa0, 0x5f, 0x5d, 0xa9, 0x3d, - 0x78, 0x75, 0xd9, 0xf8, 0xee, 0xf2, 0xe0, 0x8b, 0x40, 0xa8, 0x98, 0x84, 0x07, 0x7d, 0xf4, 0x43, - 0x58, 0xef, 0x1e, 0x9f, 0x60, 0x5c, 0xcd, 0xd5, 0xb6, 0x5e, 0x5d, 0x36, 0x1e, 0x2c, 0xe3, 0xf4, - 0x10, 0x8f, 0x99, 0x8f, 0xf9, 0xd9, 0xac, 0xd6, 0xfd, 0xf7, 0x2a, 0x38, 0xf6, 0x65, 0xff, 0xd0, - 0xdf, 0xa1, 0x8d, 0x24, 0x95, 0xa7, 0x57, 0x76, 0xf5, 0x9d, 0x19, 0xbd, 0x92, 0x10, 0xec, 0x19, - 0x3f, 0x84, 0x4a, 0x10, 0x4d, 0x3e, 0x1b, 0x50, 0x46, 0xce, 0x42, 0x5b, 0xf6, 0x96, 0xb0, 0xa3, - 0x6d, 0xbd, 0xc4, 0xa4, 0xdf, 0x8b, 0x80, 0x29, 0x2a, 0x98, 0x2d, 0x68, 0x4b, 0x78, 0xd6, 0x47, - 0x5f, 0x40, 0x3e, 0x88, 0xc8, 0xd8, 0x96, 0x21, 0x99, 0x3b, 0x38, 0xe8, 0xb7, 0x8f, 0xac, 0x06, - 0x3b, 0xa5, 0xeb, 0xab, 0xad, 0xbc, 0x36, 0x60, 0x43, 0x43, 0xf5, 0xb4, 0x12, 0xd0, 0x33, 0x99, - 0xb7, 0xbf, 0x84, 0x17, 0x2c, 0x5a, 0x47, 0x01, 0x1b, 0x0a, 0x2a, 0xa5, 0xc9, 0x02, 0x25, 0x9c, - 0x76, 0x51, 0x0d, 0x8a, 0xb6, 0x9e, 0x30, 0x05, 0x44, 0x59, 0xe7, 0x6a, 0x6b, 0xe8, 0x6c, 0x80, - 0x93, 0x44, 0x63, 0x70, 0x2e, 0xf8, 0xb8, 0xf9, 0x9f, 0x3c, 0x38, 0x7b, 0x61, 0x2c, 0x95, 0x4d, - 0x83, 0x1f, 0x2c, 0xf8, 0x2f, 0xe1, 0x0e, 0x31, 0xdf, 0x2b, 0xc2, 0x74, 0x4e, 0x31, 0x65, 0x9a, - 0x3d, 0x80, 0x47, 0x99, 0xee, 0x66, 0xe0, 0xa4, 0xa4, 0xeb, 0x14, 0xb4, 0x4f, 0x37, 0x87, 0xab, - 0xe4, 0x8d, 0x11, 0x74, 0x02, 0x1b, 0x5c, 0x78, 0x23, 0x2a, 0x55, 0x92, 0x89, 0xec, 0x77, 0x24, - 0xf3, 0xa3, 0xfa, 0x7c, 0x11, 0x68, 0x9f, 0xe1, 0x64, 0xb5, 0xcb, 0x3e, 0xd0, 0x13, 0xc8, 0x0b, - 0x72, 0x9e, 0x96, 0x9c, 0x99, 0x97, 0x04, 0x93, 0x73, 0xb5, 0xe4, 0xc2, 0x30, 0xd0, 0x6f, 0x01, - 0xfc, 0x40, 0x46, 0x44, 0x79, 0x23, 0x2a, 0xec, 0x61, 0x67, 0x6e, 0xb1, 0x3b, 0x43, 0x2d, 0x79, - 0x59, 0x60, 0xa3, 0x43, 0x28, 0x7b, 0x24, 0x95, 0x6b, 0xe1, 0xe6, 0x3f, 0xda, 0x5e, 0xdb, 0xba, - 0xa8, 0x6a, 0x17, 0xd7, 0x57, 0x5b, 0xa5, 0xd4, 0x82, 0x4b, 0x1e, 0xb1, 0xf2, 0x3d, 0x84, 0x0d, - 0xfd, 0x77, 0x1b, 0xf8, 0xf4, 0x9c, 0xc4, 0xa1, 0x4a, 0x64, 0x72, 0x43, 0x5a, 0xd1, 0x1f, 0x81, - 0xae, 0xc5, 0xd9, 0x75, 0x55, 0xd4, 0x82, 0x0d, 0xfd, 0x1e, 0xee, 0x50, 0xe6, 0x89, 0xa9, 0x11, - 0x6b, 0xba, 0xc2, 0xd2, 0xcd, 0x9b, 0xed, 0xcd, 0xc0, 0x4b, 0x9b, 0xad, 0xd2, 0x37, 0xec, 0xcd, - 0x00, 0x20, 0x49, 0xd4, 0x1f, 0x56, 0x7f, 0x08, 0xf2, 0x3e, 0x51, 0xc4, 0x48, 0xae, 0x82, 0x4d, - 0x5b, 0x4f, 0x95, 0x4c, 0xfa, 0x7f, 0x9f, 0xaa, 0xe3, 0xbe, 0xfe, 0xa6, 0xbe, 0xf2, 0x8f, 0x6f, - 0xea, 0x2b, 0x7f, 0xba, 0xae, 0xe7, 0x5e, 0x5f, 0xd7, 0x73, 0x7f, 0xbb, 0xae, 0xe7, 0xfe, 0x79, - 0x5d, 0xcf, 0x9d, 0x15, 0x4c, 0x25, 0xf5, 0x93, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x21, - 0x2b, 0x33, 0x82, 0x12, 0x00, 0x00, + // 1901 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcf, 0x73, 0x1b, 0x49, + 0x15, 0x96, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xd2, 0x24, 0x61, 0xac, 0xb0, 0xb2, 0xa2, 0x0d, + 0x59, 0xef, 0x6e, 0x21, 0x17, 0x86, 0x5a, 0xb2, 0x84, 0x05, 0x24, 0x4b, 0x38, 0xc6, 0x6b, 0x5b, + 0xd5, 0xf6, 0x06, 0x72, 0x52, 0xb5, 0x67, 0xda, 0xd2, 0x94, 0x47, 0xdd, 0x43, 0x4f, 0x8f, 0xb6, + 0x74, 0xe3, 0xb8, 0x95, 0x2b, 0x67, 0x17, 0x07, 0x4e, 0xfc, 0x27, 0x39, 0x52, 0x9c, 0x38, 0xb9, + 0x58, 0xff, 0x0b, 0xdc, 0xb8, 0x40, 0x75, 0x4f, 0x8f, 0x34, 0x4a, 0xc6, 0x9b, 0x54, 0x91, 0x03, + 0xb7, 0xee, 0xd7, 0xdf, 0xf7, 0xe6, 0xf5, 0xeb, 0xaf, 0xfb, 0x3d, 0x09, 0xac, 0x30, 0xa0, 0x4e, + 0xd8, 0x0e, 0x04, 0x97, 0x1c, 0x21, 0x97, 0x3b, 0x17, 0x54, 0xb4, 0xc3, 0xaf, 0x89, 0x98, 0x5c, + 0x78, 0xb2, 0x3d, 0xfd, 0x71, 0xdd, 0x92, 0xb3, 0x80, 0x1a, 0x40, 0xfd, 0xee, 0x88, 0x8f, 0xb8, + 0x1e, 0x6e, 0xab, 0x91, 0xb1, 0x36, 0x46, 0x9c, 0x8f, 0x7c, 0xba, 0xad, 0x67, 0x67, 0xd1, 0xf9, + 0xb6, 0x1b, 0x09, 0x22, 0x3d, 0xce, 0xcc, 0xfa, 0xc6, 0xeb, 0xeb, 0x84, 0xcd, 0xe2, 0xa5, 0xd6, + 0x65, 0x01, 0xca, 0x47, 0xdc, 0xa5, 0x27, 0x01, 0x75, 0xd0, 0x1e, 0x58, 0x84, 0x31, 0x2e, 0x35, + 0x37, 0xb4, 0xf3, 0xcd, 0xfc, 0x96, 0xb5, 0xb3, 0xd9, 0x7e, 0x33, 0xa8, 0x76, 0x67, 0x01, 0xeb, + 0x16, 0x5e, 0x5d, 0x6d, 0xe6, 0x70, 0x9a, 0x89, 0x7e, 0x05, 0x55, 0x97, 0x86, 0x9e, 0xa0, 0xee, + 0x50, 0x70, 0x9f, 0xda, 0x2b, 0xcd, 0xfc, 0xd6, 0xad, 0x9d, 0x1f, 0x64, 0x79, 0x52, 0x1f, 0xc7, + 0xdc, 0xa7, 0xd8, 0x32, 0x0c, 0x35, 0x41, 0x7b, 0x00, 0x13, 0x3a, 0x39, 0xa3, 0x22, 0x1c, 0x7b, + 0x81, 0xbd, 0xaa, 0xe9, 0x1f, 0xdd, 0x44, 0x57, 0xb1, 0xb7, 0x0f, 0xe7, 0x70, 0x9c, 0xa2, 0xa2, + 0x43, 0xa8, 0x92, 0x29, 0xf1, 0x7c, 0x72, 0xe6, 0xf9, 0x9e, 0x9c, 0xd9, 0x05, 0xed, 0xea, 0xe3, + 0xef, 0x74, 0xd5, 0x49, 0x11, 0xf0, 0x12, 0xbd, 0xe5, 0x02, 0x2c, 0x3e, 0x84, 0x1e, 0x43, 0x69, + 0xd0, 0x3f, 0xea, 0xed, 0x1f, 0xed, 0xd5, 0x72, 0xf5, 0x8d, 0x97, 0x97, 0xcd, 0x7b, 0xca, 0xc7, + 0x02, 0x30, 0xa0, 0xcc, 0xf5, 0xd8, 0x08, 0x6d, 0x41, 0xb9, 0xb3, 0xbb, 0xdb, 0x1f, 0x9c, 0xf6, + 0x7b, 0xb5, 0x7c, 0xbd, 0xfe, 0xf2, 0xb2, 0x79, 0x7f, 0x19, 0xd8, 0x71, 0x1c, 0x1a, 0x48, 0xea, + 0xd6, 0x0b, 0xdf, 0xfc, 0xa5, 0x91, 0x6b, 0x7d, 0x93, 0x87, 0x6a, 0x3a, 0x08, 0xf4, 0x18, 0x8a, + 0x9d, 0xdd, 0xd3, 0xfd, 0xe7, 0xfd, 0x5a, 0x6e, 0x41, 0x4f, 0x23, 0x3a, 0x8e, 0xf4, 0xa6, 0x14, + 0x3d, 0x82, 0xb5, 0x41, 0xe7, 0xab, 0x93, 0x7e, 0x2d, 0xbf, 0x08, 0x27, 0x0d, 0x1b, 0x90, 0x28, + 0xd4, 0xa8, 0x1e, 0xee, 0xec, 0x1f, 0xd5, 0x56, 0xb2, 0x51, 0x3d, 0x41, 0x3c, 0x66, 0x42, 0xf9, + 0x73, 0x01, 0xac, 0x13, 0x2a, 0xa6, 0x9e, 0xf3, 0x9e, 0x25, 0xf2, 0x19, 0x14, 0x24, 0x09, 0x2f, + 0xb4, 0x34, 0xac, 0x6c, 0x69, 0x9c, 0x92, 0xf0, 0x42, 0x7d, 0xd4, 0xd0, 0x35, 0x5e, 0x29, 0x43, + 0xd0, 0xc0, 0xf7, 0x1c, 0x22, 0xa9, 0xab, 0x95, 0x61, 0xed, 0xfc, 0x30, 0x8b, 0x8d, 0xe7, 0x28, + 0x13, 0xff, 0xb3, 0x1c, 0x4e, 0x51, 0xd1, 0x53, 0x28, 0x8e, 0x7c, 0x7e, 0x46, 0x7c, 0xad, 0x09, + 0x6b, 0xe7, 0x61, 0x96, 0x93, 0x3d, 0x8d, 0x58, 0x38, 0x30, 0x14, 0xf4, 0x04, 0x8a, 0x51, 0xe0, + 0x12, 0x49, 0xed, 0xa2, 0x26, 0x37, 0xb3, 0xc8, 0x5f, 0x69, 0xc4, 0x2e, 0x67, 0xe7, 0xde, 0x08, + 0x1b, 0x3c, 0x3a, 0x80, 0x32, 0xa3, 0xf2, 0x6b, 0x2e, 0x2e, 0x42, 0xbb, 0xd4, 0x5c, 0xdd, 0xb2, + 0x76, 0x3e, 0xcd, 0x14, 0x63, 0x8c, 0xe9, 0x48, 0x49, 0x9c, 0xf1, 0x84, 0x32, 0x19, 0xbb, 0xe9, + 0xae, 0xd8, 0x79, 0x3c, 0x77, 0x80, 0x7e, 0x01, 0x65, 0xca, 0xdc, 0x80, 0x7b, 0x4c, 0xda, 0xe5, + 0x9b, 0x03, 0xe9, 0x1b, 0x8c, 0x4a, 0x26, 0x9e, 0x33, 0x14, 0x5b, 0x70, 0xdf, 0x3f, 0x23, 0xce, + 0x85, 0x5d, 0x79, 0xc7, 0x6d, 0xcc, 0x19, 0xdd, 0x22, 0x14, 0x26, 0xdc, 0xa5, 0xad, 0x6d, 0xb8, + 0xf3, 0x46, 0xaa, 0x51, 0x1d, 0xca, 0x26, 0xd5, 0xb1, 0x46, 0x0a, 0x78, 0x3e, 0x6f, 0xdd, 0x86, + 0xf5, 0xa5, 0xb4, 0xb6, 0xfe, 0x5e, 0x80, 0x72, 0x72, 0xd6, 0xa8, 0x03, 0x15, 0x87, 0x33, 0x49, + 0x3c, 0x46, 0x85, 0x91, 0x57, 0xe6, 0xc9, 0xec, 0x26, 0x20, 0xc5, 0x7a, 0x96, 0xc3, 0x0b, 0x16, + 0xfa, 0x0d, 0x54, 0x04, 0x0d, 0x79, 0x24, 0x1c, 0x1a, 0x1a, 0x7d, 0x6d, 0x65, 0x2b, 0x24, 0x06, + 0x61, 0xfa, 0x87, 0xc8, 0x13, 0x54, 0x65, 0x39, 0xc4, 0x0b, 0x2a, 0x7a, 0x0a, 0x25, 0x41, 0x43, + 0x49, 0x84, 0xfc, 0x2e, 0x89, 0xe0, 0x18, 0x32, 0xe0, 0xbe, 0xe7, 0xcc, 0x70, 0xc2, 0x40, 0x4f, + 0xa1, 0x12, 0xf8, 0xc4, 0xd1, 0x5e, 0xed, 0x35, 0x4d, 0xff, 0x20, 0x8b, 0x3e, 0x48, 0x40, 0x78, + 0x81, 0x47, 0x9f, 0x03, 0xf8, 0x7c, 0x34, 0x74, 0x85, 0x37, 0xa5, 0xc2, 0x48, 0xac, 0x9e, 0xc5, + 0xee, 0x69, 0x04, 0xae, 0xf8, 0x7c, 0x14, 0x0f, 0xd1, 0xde, 0xff, 0xa4, 0xaf, 0x94, 0xb6, 0x0e, + 0x00, 0xc8, 0x7c, 0xd5, 0xa8, 0xeb, 0xe3, 0x77, 0x72, 0x65, 0x4e, 0x24, 0x45, 0x47, 0x0f, 0xa1, + 0x7a, 0xce, 0x85, 0x43, 0x87, 0xe6, 0xd6, 0x54, 0xb4, 0x26, 0x2c, 0x6d, 0x8b, 0xf5, 0x85, 0xba, + 0x50, 0x1a, 0x51, 0x46, 0x85, 0xe7, 0xd8, 0xa0, 0x3f, 0xf6, 0x38, 0xf3, 0x42, 0xc6, 0x10, 0x1c, + 0x31, 0xe9, 0x4d, 0xa8, 0xf9, 0x52, 0x42, 0xec, 0x56, 0xa0, 0x24, 0xe2, 0x95, 0xd6, 0xef, 0x01, + 0xbd, 0x89, 0x45, 0x08, 0x0a, 0x17, 0x1e, 0x73, 0xb5, 0xb0, 0x2a, 0x58, 0x8f, 0x51, 0x1b, 0x4a, + 0x01, 0x99, 0xf9, 0x9c, 0xb8, 0x46, 0x2c, 0x77, 0xdb, 0x71, 0xbd, 0x6c, 0x27, 0xf5, 0xb2, 0xdd, + 0x61, 0x33, 0x9c, 0x80, 0x5a, 0x07, 0x70, 0x2f, 0x73, 0xcb, 0x68, 0x07, 0xaa, 0x73, 0x11, 0x0e, + 0x3d, 0xf3, 0x91, 0xee, 0xed, 0xeb, 0xab, 0x4d, 0x6b, 0xae, 0xd6, 0xfd, 0x1e, 0xb6, 0xe6, 0xa0, + 0x7d, 0xb7, 0xf5, 0xa7, 0x0a, 0xac, 0x2f, 0x49, 0x19, 0xdd, 0x85, 0x35, 0x6f, 0x42, 0x46, 0xd4, + 0xc4, 0x18, 0x4f, 0x50, 0x1f, 0x8a, 0x3e, 0x39, 0xa3, 0xbe, 0x12, 0xb4, 0x3a, 0xd4, 0x1f, 0xbd, + 0xf5, 0x4e, 0xb4, 0xbf, 0xd4, 0xf8, 0x3e, 0x93, 0x62, 0x86, 0x0d, 0x19, 0xd9, 0x50, 0x72, 0xf8, + 0x64, 0x42, 0x98, 0x7a, 0x3a, 0x57, 0xb7, 0x2a, 0x38, 0x99, 0xaa, 0xcc, 0x10, 0x31, 0x0a, 0xed, + 0x82, 0x36, 0xeb, 0x31, 0xaa, 0xc1, 0x2a, 0x65, 0x53, 0x7b, 0x4d, 0x9b, 0xd4, 0x50, 0x59, 0x5c, + 0x2f, 0x56, 0x64, 0x05, 0xab, 0xa1, 0xe2, 0x45, 0x21, 0x15, 0x76, 0x29, 0xce, 0xa8, 0x1a, 0xa3, + 0x9f, 0x41, 0x71, 0xc2, 0x23, 0x26, 0x43, 0xbb, 0xac, 0x83, 0xdd, 0xc8, 0x0a, 0xf6, 0x50, 0x21, + 0xcc, 0xd3, 0x6e, 0xe0, 0xa8, 0x0f, 0x77, 0x42, 0xc9, 0x83, 0xe1, 0x48, 0x10, 0x87, 0x0e, 0x03, + 0x2a, 0x3c, 0xee, 0x9a, 0xa7, 0x69, 0xe3, 0x8d, 0x43, 0xe9, 0x99, 0x26, 0x07, 0xdf, 0x56, 0x9c, + 0x3d, 0x45, 0x19, 0x68, 0x06, 0x1a, 0x40, 0x35, 0x88, 0x7c, 0x7f, 0xc8, 0x83, 0xb8, 0x4a, 0xc5, + 0x7a, 0x7a, 0x87, 0x94, 0x0d, 0x22, 0xdf, 0x3f, 0x8e, 0x49, 0xd8, 0x0a, 0x16, 0x13, 0x74, 0x1f, + 0x8a, 0x23, 0xc1, 0xa3, 0x20, 0xb4, 0x2d, 0x9d, 0x0c, 0x33, 0x43, 0x5f, 0x40, 0x29, 0xa4, 0x8e, + 0xa0, 0x32, 0xb4, 0xab, 0x7a, 0xab, 0x1f, 0x66, 0x7d, 0xe4, 0x44, 0x43, 0x30, 0x3d, 0xa7, 0x82, + 0x32, 0x87, 0xe2, 0x84, 0x83, 0x36, 0x60, 0x55, 0xca, 0x99, 0xbd, 0xde, 0xcc, 0x6f, 0x95, 0xbb, + 0xa5, 0xeb, 0xab, 0xcd, 0xd5, 0xd3, 0xd3, 0x17, 0x58, 0xd9, 0xd4, 0x0b, 0x3a, 0xe6, 0xa1, 0x64, + 0x64, 0x42, 0xed, 0x5b, 0x3a, 0xb7, 0xf3, 0x39, 0x7a, 0x01, 0xe0, 0xb2, 0x70, 0xe8, 0xe8, 0x2b, + 0x6b, 0xdf, 0xd6, 0xbb, 0xfb, 0xf4, 0xed, 0xbb, 0xeb, 0x1d, 0x9d, 0x98, 0x2a, 0xb2, 0x7e, 0x7d, + 0xb5, 0x59, 0x99, 0x4f, 0x71, 0xc5, 0x65, 0x61, 0x3c, 0x44, 0x5d, 0xb0, 0xc6, 0x94, 0xf8, 0x72, + 0xec, 0x8c, 0xa9, 0x73, 0x61, 0xd7, 0x6e, 0x2e, 0x0b, 0xcf, 0x34, 0xcc, 0x78, 0x48, 0x93, 0x94, + 0x82, 0x55, 0xa8, 0xa1, 0x7d, 0x47, 0xe7, 0x2a, 0x9e, 0xa0, 0x0f, 0x00, 0x78, 0x40, 0xd9, 0x30, + 0x94, 0xae, 0xc7, 0x6c, 0xa4, 0xb6, 0x8c, 0x2b, 0xca, 0x72, 0xa2, 0x0c, 0xe8, 0x81, 0x7a, 0xb4, + 0x89, 0x3b, 0xe4, 0xcc, 0x9f, 0xd9, 0xdf, 0xd3, 0xab, 0x65, 0x65, 0x38, 0x66, 0xfe, 0x0c, 0x6d, + 0x82, 0xa5, 0x75, 0x11, 0x7a, 0x23, 0x46, 0x7c, 0xfb, 0xae, 0xce, 0x07, 0x28, 0xd3, 0x89, 0xb6, + 0xa8, 0x73, 0x88, 0xb3, 0x11, 0xda, 0xf7, 0x6e, 0x3e, 0x07, 0x13, 0xec, 0xe2, 0x1c, 0x0c, 0x07, + 0xfd, 0x12, 0x20, 0x10, 0xde, 0xd4, 0xf3, 0xe9, 0x88, 0x86, 0xf6, 0x7d, 0xbd, 0xe9, 0x46, 0xe6, + 0x6b, 0x3d, 0x47, 0xe1, 0x14, 0xa3, 0xfe, 0x39, 0x58, 0xa9, 0xdb, 0xa6, 0x6e, 0xc9, 0x05, 0x9d, + 0x99, 0x0b, 0xac, 0x86, 0x2a, 0x25, 0x53, 0xe2, 0x47, 0x71, 0x27, 0x5c, 0xc1, 0xf1, 0xe4, 0xe7, + 0x2b, 0x4f, 0xf2, 0xf5, 0x1d, 0xb0, 0x52, 0xaa, 0x43, 0x1f, 0xc2, 0xba, 0xa0, 0x23, 0x2f, 0x94, + 0x62, 0x36, 0x24, 0x91, 0x1c, 0xdb, 0xbf, 0xd6, 0x84, 0x6a, 0x62, 0xec, 0x44, 0x72, 0x5c, 0x1f, + 0xc2, 0xe2, 0xf0, 0x50, 0x13, 0x2c, 0x25, 0x8a, 0x90, 0x8a, 0x29, 0x15, 0xaa, 0xda, 0xaa, 0x9c, + 0xa7, 0x4d, 0x4a, 0xbc, 0x21, 0x25, 0xc2, 0x19, 0xeb, 0xb7, 0xa3, 0x82, 0xcd, 0x4c, 0x3d, 0x06, + 0xc9, 0x0d, 0x31, 0x8f, 0x81, 0x99, 0xb6, 0xfe, 0x95, 0x87, 0x6a, 0xba, 0x69, 0x40, 0xbb, 0x71, + 0xb1, 0xd7, 0x5b, 0xba, 0xb5, 0xb3, 0xfd, 0xb6, 0x26, 0x43, 0x97, 0x56, 0x3f, 0x52, 0xce, 0x0e, + 0x55, 0x7f, 0xaf, 0xc9, 0xe8, 0xa7, 0xb0, 0x16, 0x70, 0x21, 0x93, 0x27, 0x2c, 0x3b, 0xc1, 0x5c, + 0x24, 0xa5, 0x28, 0x06, 0xb7, 0xc6, 0x70, 0x6b, 0xd9, 0x1b, 0x7a, 0x04, 0xab, 0xcf, 0xf7, 0x07, + 0xb5, 0x5c, 0xfd, 0xc1, 0xcb, 0xcb, 0xe6, 0xf7, 0x97, 0x17, 0x9f, 0x7b, 0x42, 0x46, 0xc4, 0xdf, + 0x1f, 0xa0, 0x4f, 0x60, 0xad, 0x77, 0x74, 0x82, 0x71, 0x2d, 0x5f, 0xdf, 0x7c, 0x79, 0xd9, 0x7c, + 0xb0, 0x8c, 0x53, 0x4b, 0x3c, 0x62, 0x2e, 0xe6, 0x67, 0xf3, 0x5e, 0xf7, 0xdf, 0x2b, 0x60, 0x99, + 0x97, 0xfd, 0x7d, 0xff, 0x1c, 0x5a, 0x8f, 0x4b, 0x79, 0x72, 0x65, 0x57, 0xde, 0x5a, 0xd1, 0xab, + 0x31, 0xc1, 0x9c, 0xf1, 0x43, 0xa8, 0x7a, 0xc1, 0xf4, 0xb3, 0x21, 0x65, 0xe4, 0xcc, 0x37, 0x6d, + 0x6f, 0x19, 0x5b, 0xca, 0xd6, 0x8f, 0x4d, 0xea, 0xbd, 0xf0, 0x98, 0xa4, 0x82, 0x99, 0x86, 0xb6, + 0x8c, 0xe7, 0x73, 0xf4, 0x05, 0x14, 0xbc, 0x80, 0x4c, 0x4c, 0x1b, 0x92, 0xb9, 0x83, 0xfd, 0x41, + 0xe7, 0xd0, 0x68, 0xb0, 0x5b, 0xbe, 0xbe, 0xda, 0x2c, 0x28, 0x03, 0xd6, 0x34, 0xd4, 0x48, 0x3a, + 0x01, 0xf5, 0x25, 0xfd, 0xf6, 0x97, 0x71, 0xca, 0xa2, 0x74, 0xe4, 0xb1, 0x91, 0xa0, 0x61, 0xa8, + 0xab, 0x40, 0x19, 0x27, 0x53, 0x54, 0x87, 0x92, 0xe9, 0x27, 0x74, 0x03, 0x51, 0x51, 0xb5, 0xda, + 0x18, 0xba, 0xeb, 0x60, 0xc5, 0xd9, 0x18, 0x9e, 0x0b, 0x3e, 0x69, 0xfd, 0xa7, 0x00, 0xd6, 0xae, + 0x1f, 0x85, 0xd2, 0x94, 0xc1, 0xf7, 0x96, 0xfc, 0x17, 0x70, 0x87, 0xe8, 0x9f, 0x57, 0x84, 0xa9, + 0x9a, 0xa2, 0xdb, 0x34, 0x73, 0x00, 0x8f, 0x32, 0xdd, 0xcd, 0xc1, 0x71, 0x4b, 0xd7, 0x2d, 0x2a, + 0x9f, 0x76, 0x1e, 0xd7, 0xc8, 0x6b, 0x2b, 0xe8, 0x04, 0xd6, 0xb9, 0x70, 0xc6, 0x34, 0x94, 0x71, + 0x25, 0x32, 0x3f, 0x47, 0x32, 0x7f, 0xa8, 0x1e, 0xa7, 0x81, 0xe6, 0x19, 0x8e, 0xa3, 0x5d, 0xf6, + 0x81, 0x9e, 0x40, 0x41, 0x90, 0xf3, 0xa4, 0xe5, 0xcc, 0xbc, 0x24, 0x98, 0x9c, 0xcb, 0x25, 0x17, + 0x9a, 0x81, 0x7e, 0x0b, 0xe0, 0x7a, 0x61, 0x40, 0xa4, 0x33, 0xa6, 0xc2, 0x1c, 0x76, 0xe6, 0x16, + 0x7b, 0x73, 0xd4, 0x92, 0x97, 0x14, 0x1b, 0x1d, 0x40, 0xc5, 0x21, 0x89, 0x5c, 0x8b, 0x37, 0xff, + 0x46, 0xdb, 0xed, 0x18, 0x17, 0x35, 0xe5, 0xe2, 0xfa, 0x6a, 0xb3, 0x9c, 0x58, 0x70, 0xd9, 0x21, + 0x46, 0xbe, 0x07, 0xb0, 0xae, 0x7e, 0xbb, 0x0d, 0x5d, 0x7a, 0x4e, 0x22, 0x5f, 0xc6, 0x32, 0xb9, + 0xa1, 0xac, 0xa8, 0x1f, 0x02, 0x3d, 0x83, 0x33, 0x71, 0x55, 0x65, 0xca, 0x86, 0x7e, 0x07, 0x77, + 0x28, 0x73, 0xc4, 0x4c, 0x8b, 0x35, 0x89, 0xb0, 0x7c, 0xf3, 0x66, 0xfb, 0x73, 0xf0, 0xd2, 0x66, + 0x6b, 0xf4, 0x35, 0x7b, 0xeb, 0xaf, 0x79, 0x80, 0xb8, 0x52, 0xbf, 0x5f, 0x01, 0x22, 0x28, 0xb8, + 0x44, 0x12, 0xad, 0xb9, 0x2a, 0xd6, 0x63, 0x55, 0x70, 0x24, 0x9d, 0x04, 0x3e, 0x91, 0x1e, 0x1b, + 0x99, 0xff, 0x37, 0x32, 0x8f, 0xfa, 0x74, 0x8e, 0xc2, 0x29, 0x86, 0x8e, 0x35, 0x0e, 0xfb, 0xff, + 0x3e, 0xd6, 0x4f, 0x3e, 0x02, 0x58, 0xac, 0xa0, 0x32, 0x14, 0x8e, 0x8e, 0x8f, 0xfa, 0xb5, 0x1c, + 0xba, 0x0d, 0xd6, 0xde, 0xf1, 0xf0, 0xb4, 0x7f, 0x38, 0xf8, 0xb2, 0x73, 0xda, 0xaf, 0xe5, 0xbb, + 0xf6, 0xab, 0x6f, 0x1b, 0xb9, 0x7f, 0x7c, 0xdb, 0xc8, 0xfd, 0xf1, 0xba, 0x91, 0x7f, 0x75, 0xdd, + 0xc8, 0xff, 0xed, 0xba, 0x91, 0xff, 0xe7, 0x75, 0x23, 0x7f, 0x56, 0xd4, 0x4d, 0xdf, 0x4f, 0xfe, + 0x1b, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x7b, 0xf5, 0x7d, 0x2d, 0x13, 0x00, 0x00, } diff --git a/api/specs.proto b/api/specs.proto index f7a4d18b2e..31d3873c47 100644 --- a/api/specs.proto +++ b/api/specs.proto @@ -380,12 +380,23 @@ message ClusterSpec { EncryptionConfig encryption_config = 8 [(gogoproto.nullable) = false]; } +// Templating controls whether the payload of a config or secret is evaluated +// as a template. +enum Templating { + NONE = 0; + GO_TEMPLATE = 1; +} + // SecretSpec specifies a user-provided secret. message SecretSpec { Annotations annotations = 1 [(gogoproto.nullable) = false]; // Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes) bytes data = 2; + + // Templating controls whether and how to evaluate the secret payload as + // a template. + Templating templating = 3; } // ConfigSpec specifies user-provided configuration files. @@ -396,4 +407,8 @@ message ConfigSpec { // TODO(aaronl): Do we want to revise this to include multiple payloads in a single // ConfigSpec? Define this to be a tar? etc... bytes data = 2; + + // Templating controls whether and how to evaluate the config payload as + // a template. + Templating templating = 3; } diff --git a/template/context.go b/template/context.go index e3c3aab113..4afa1fc8c5 100644 --- a/template/context.go +++ b/template/context.go @@ -3,13 +3,22 @@ package template import ( "bytes" "fmt" + "strings" + "text/template" + "github.com/docker/swarmkit/agent/configs" + "github.com/docker/swarmkit/agent/exec" + "github.com/docker/swarmkit/agent/secrets" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/naming" + "github.com/pkg/errors" ) // Context defines the strict set of values that can be injected into a // template expression in SwarmKit data structure. +// NOTE: Be very careful adding any fields to this structure with types +// that have methods defined on them. The template would be able to +// invoke those methods. type Context struct { Service struct { ID string @@ -58,7 +67,166 @@ func NewContextFromTask(t *api.Task) (ctx Context) { // Expand treats the string s as a template and populates it with values from // the context. func (ctx *Context) Expand(s string) (string, error) { - tmpl, err := newTemplate(s) + tmpl, err := newTemplate(s, nil) + if err != nil { + return s, err + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, ctx); err != nil { + return s, err + } + + return buf.String(), nil +} + +// PayloadContext provides a context for expanding a config or secret payload. +// NOTE: Be very careful adding any fields to this structure with types +// that have methods defined on them. The template would be able to +// invoke those methods. +type PayloadContext struct { + Context + + t *api.Task + restrictedSecrets exec.SecretGetter + restrictedConfigs exec.ConfigGetter +} + +func (ctx PayloadContext) secretGetterBySource(source string) (string, error) { + if ctx.restrictedSecrets == nil { + return "", errors.New("secrets unavailable") + } + + container := ctx.t.Spec.GetContainer() + if container == nil { + return "", errors.New("task is not a container") + } + + for _, secretRef := range container.Secrets { + if secretRef.SecretName == source { + secret := ctx.restrictedSecrets.Get(secretRef.SecretID) + if secret != nil { + return string(secret.Spec.Data), nil + } + break + } + } + + return "", errors.Errorf("secret source %s not found", source) +} + +func (ctx PayloadContext) secretGetterByTarget(target string) (string, error) { + if ctx.restrictedSecrets == nil { + return "", errors.New("secrets unavailable") + } + + container := ctx.t.Spec.GetContainer() + if container == nil { + return "", errors.New("task is not a container") + } + + for _, secretRef := range container.Secrets { + file := secretRef.GetFile() + if file != nil && file.Name == target { + secret := ctx.restrictedSecrets.Get(secretRef.SecretID) + if secret != nil { + return string(secret.Spec.Data), nil + } + break + } + } + + return "", errors.Errorf("secret target %s not found", target) +} + +func (ctx PayloadContext) configGetterBySource(source string) (string, error) { + if ctx.restrictedConfigs == nil { + return "", errors.New("configs unavailable") + } + + container := ctx.t.Spec.GetContainer() + if container == nil { + return "", errors.New("task is not a container") + } + + for _, configRef := range container.Configs { + if configRef.ConfigName == source { + config := ctx.restrictedConfigs.Get(configRef.ConfigID) + if config != nil { + return string(config.Spec.Data), nil + } + break + } + } + + return "", errors.Errorf("config source %s not found", source) +} + +func (ctx PayloadContext) configGetterByTarget(target string) (string, error) { + if ctx.restrictedConfigs == nil { + return "", errors.New("configs unvailable") + } + + container := ctx.t.Spec.GetContainer() + if container == nil { + return "", errors.New("task is not a container") + } + + for _, configRef := range container.Configs { + file := configRef.GetFile() + if file != nil && file.Name == target { + config := ctx.restrictedConfigs.Get(configRef.ConfigID) + if config != nil { + return string(config.Spec.Data), nil + } + break + } + } + + return "", errors.Errorf("config target %s not found", target) +} + +func (ctx PayloadContext) envGetter(variable string) (string, error) { + container := ctx.t.Spec.GetContainer() + if container == nil { + return "", errors.New("task is not a container") + } + + for _, env := range container.Env { + parts := strings.SplitN(env, "=", 2) + + if len(parts) > 1 && parts[0] == variable { + return parts[1], nil + } + } + return "", nil +} + +// NewPayloadContextFromTask returns a new template context from the data +// available in the task. This context also provides access to the configs +// and secrets that the task has access to. The provided context can then +// be used to populate runtime values in a templated config or secret. +func NewPayloadContextFromTask(t *api.Task, dependencies exec.DependencyGetter) (ctx PayloadContext) { + return PayloadContext{ + Context: NewContextFromTask(t), + t: t, + restrictedSecrets: secrets.Restrict(dependencies.Secrets(), t), + restrictedConfigs: configs.Restrict(dependencies.Configs(), t), + } +} + +// Expand treats the string s as a template and populates it with values from +// the context. +func (ctx *PayloadContext) Expand(s string) (string, error) { + funcMap := template.FuncMap{ + "SecretBySource": ctx.secretGetterBySource, + "Secret": ctx.secretGetterByTarget, + "ConfigBySource": ctx.configGetterBySource, + "Config": ctx.configGetterByTarget, + "Env": ctx.envGetter, + } + + tmpl, err := newTemplate(s, funcMap) if err != nil { return s, err } diff --git a/template/expand.go b/template/expand.go index 75fbc09aee..fc8e18f074 100644 --- a/template/expand.go +++ b/template/expand.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/pkg/errors" ) @@ -116,3 +117,47 @@ func expandEnv(ctx Context, values []string) ([]string, error) { return result, nil } + +func expandPayload(ctx PayloadContext, payload []byte) ([]byte, error) { + result, err := ctx.Expand(string(payload)) + if err != nil { + return payload, err + } + return []byte(result), nil +} + +// ExpandSecretSpec expands the template inside the secret payload, if any. +// Templating is evaluated on the agent-side. +func ExpandSecretSpec(s *api.Secret, t *api.Task, dependencies exec.DependencyGetter) (*api.SecretSpec, error) { + switch s.Spec.Templating { + case api.Templating_NONE: + return &s.Spec, nil + case api.Templating_GO_TEMPLATE: + ctx := NewPayloadContextFromTask(t, dependencies) + secretSpec := s.Spec.Copy() + + var err error + secretSpec.Data, err = expandPayload(ctx, secretSpec.Data) + return secretSpec, err + default: + return &s.Spec, errors.New("unrecognized template type") + } +} + +// ExpandConfigSpec expands the template inside the config payload, if any. +// Templating is evaluated on the agent-side. +func ExpandConfigSpec(c *api.Config, t *api.Task, dependencies exec.DependencyGetter) (*api.ConfigSpec, error) { + switch c.Spec.Templating { + case api.Templating_NONE: + return &c.Spec, nil + case api.Templating_GO_TEMPLATE: + ctx := NewPayloadContextFromTask(t, dependencies) + configSpec := c.Spec.Copy() + + var err error + configSpec.Data, err = expandPayload(ctx, configSpec.Data) + return configSpec, err + default: + return &c.Spec, errors.New("unrecognized template type") + } +} diff --git a/template/getter.go b/template/getter.go new file mode 100644 index 0000000000..9b0e377477 --- /dev/null +++ b/template/getter.go @@ -0,0 +1,100 @@ +package template + +import ( + "github.com/docker/swarmkit/agent/exec" + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/log" +) + +type templatedSecretGetter struct { + dependencies exec.DependencyGetter + t *api.Task +} + +// NewTemplatedSecretGetter returns a SecretGetter that evaluates templates. +func NewTemplatedSecretGetter(dependencies exec.DependencyGetter, t *api.Task) exec.SecretGetter { + return templatedSecretGetter{dependencies: dependencies, t: t} +} + +func (t templatedSecretGetter) Get(secretID string) *api.Secret { + if t.dependencies == nil { + return nil + } + + secrets := t.dependencies.Secrets() + if secrets == nil { + return nil + } + + secret := secrets.Get(secretID) + if secret == nil { + return nil + } + + newSpec, err := ExpandSecretSpec(secret, t.t, t.dependencies) + if err != nil { + log.L.WithError(err).Error("failed to expand templated secret") + return secret + } + + secretCopy := *secret + secretCopy.Spec = *newSpec + return &secretCopy +} + +type templatedConfigGetter struct { + dependencies exec.DependencyGetter + t *api.Task +} + +// NewTemplatedConfigGetter returns a ConfigGetter that evaluates templates. +func NewTemplatedConfigGetter(dependencies exec.DependencyGetter, t *api.Task) exec.ConfigGetter { + return templatedConfigGetter{dependencies: dependencies, t: t} +} + +func (t templatedConfigGetter) Get(configID string) *api.Config { + if t.dependencies == nil { + return nil + } + + configs := t.dependencies.Configs() + if configs == nil { + return nil + } + + config := configs.Get(configID) + if config == nil { + return nil + } + + newSpec, err := ExpandConfigSpec(config, t.t, t.dependencies) + if err != nil { + log.L.WithError(err).Error("failed to expand templated config") + return config + } + + configCopy := *config + configCopy.Spec = *newSpec + return &configCopy +} + +type templatedDependencyGetter struct { + secrets exec.SecretGetter + configs exec.ConfigGetter +} + +// NewTemplatedDependencyGetter returns a DependencyGetter that evaluates templates. +func NewTemplatedDependencyGetter(dependencies exec.DependencyGetter, t *api.Task) exec.DependencyGetter { + return templatedDependencyGetter{ + secrets: NewTemplatedSecretGetter(dependencies, t), + configs: NewTemplatedConfigGetter(dependencies, t), + } +} + +func (t templatedDependencyGetter) Secrets() exec.SecretGetter { + return t.secrets +} + +func (t templatedDependencyGetter) Configs() exec.ConfigGetter { + return t.configs +} diff --git a/template/getter_test.go b/template/getter_test.go new file mode 100644 index 0000000000..e0af65e1a4 --- /dev/null +++ b/template/getter_test.go @@ -0,0 +1,586 @@ +package template + +import ( + "testing" + + "github.com/docker/swarmkit/agent" + "github.com/docker/swarmkit/api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTemplatedSecret(t *testing.T) { + templatedSecret := &api.Secret{ + ID: "templatedsecret", + } + + referencedSecret := &api.Secret{ + ID: "referencedsecret", + Spec: api.SecretSpec{ + Data: []byte("mysecret"), + }, + } + referencedConfig := &api.Config{ + ID: "referencedconfig", + Spec: api.ConfigSpec{ + Data: []byte("myconfig"), + }, + } + + type testCase struct { + desc string + secretSpec api.SecretSpec + task *api.Task + expected string + } + + testCases := []testCase{ + { + desc: "Test expansion of task context", + secretSpec: api.SecretSpec{ + Data: []byte("SERVICE_ID={{.Service.ID}}\n" + + "SERVICE_NAME={{.Service.Name}}\n" + + "TASK_ID={{.Task.ID}}\n" + + "TASK_NAME={{.Task.Name}}\n" + + "NODE_ID={{.Node.ID}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SERVICE_ID=serviceID\n" + + "SERVICE_NAME=serviceName\n" + + "TASK_ID=taskID\n" + + "TASK_NAME=serviceName.10.taskID\n" + + "NODE_ID=nodeID\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret, by source", + secretSpec: api.SecretSpec{ + Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL=mysecret\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + { + SecretID: "referencedsecret", + SecretName: "referencedsecretname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret, by target", + secretSpec: api.SecretSpec{ + Data: []byte("SECRET_VAL={{Secret \"referencedsecrettarget\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL=mysecret\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + { + SecretID: "referencedsecret", + SecretName: "referencedsecretname", + Target: &api.SecretReference_File{ + File: &api.FileTarget{ + Name: "referencedsecrettarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config, by source", + secretSpec: api.SecretSpec{ + Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL=myconfig\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + Configs: []*api.ConfigReference{ + { + ConfigID: "referencedconfig", + ConfigName: "referencedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config, by target", + secretSpec: api.SecretSpec{ + Data: []byte("CONFIG_VAL={{Config \"referencedconfigtarget\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL=myconfig\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + Configs: []*api.ConfigReference{ + { + ConfigID: "referencedconfig", + ConfigName: "referencedconfigname", + Target: &api.ConfigReference_File{ + File: &api.FileTarget{ + Name: "referencedconfigtarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret not available to task", + secretSpec: api.SecretSpec{ + Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config not available to task", + secretSpec: api.SecretSpec{ + Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test that expansion of the same secret avoids recursion", + secretSpec: api.SecretSpec{ + Data: []byte("SECRET_VAL={{SecretBySource \"templatedsecretname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL=SECRET_VAL={{SecretBySource \"templatedsecretname\"}}\n\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test env", + secretSpec: api.SecretSpec{ + Data: []byte("ENV VALUE={{Env \"foo\"}}\n" + + "DOES NOT EXIST={{Env \"badname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "ENV VALUE=bar\n" + + "DOES NOT EXIST=\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "templatedsecret", + SecretName: "templatedsecretname", + }, + }, + Env: []string{"foo=bar"}, + }, + }, + } + }), + }, + } + + for _, testCase := range testCases { + templatedSecret.Spec = testCase.secretSpec + + dependencyManager := agent.NewDependencyManager() + dependencyManager.Secrets().Add(*templatedSecret, *referencedSecret) + dependencyManager.Configs().Add(*referencedConfig) + + templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task) + expandedSecret := templatedDependencies.Secrets().Get("templatedsecret") + + require.NotNil(t, expandedSecret) + assert.Equal(t, testCase.expected, string(expandedSecret.Spec.Data), testCase.desc) + } +} + +func TestTemplatedConfig(t *testing.T) { + templatedConfig := &api.Config{ + ID: "templatedconfig", + } + + referencedSecret := &api.Secret{ + ID: "referencedsecret", + Spec: api.SecretSpec{ + Data: []byte("mysecret"), + }, + } + referencedConfig := &api.Config{ + ID: "referencedconfig", + Spec: api.ConfigSpec{ + Data: []byte("myconfig"), + }, + } + + type testCase struct { + desc string + configSpec api.ConfigSpec + task *api.Task + expected string + } + + testCases := []testCase{ + { + desc: "Test expansion of task context", + configSpec: api.ConfigSpec{ + Data: []byte("SERVICE_ID={{.Service.ID}}\n" + + "SERVICE_NAME={{.Service.Name}}\n" + + "TASK_ID={{.Task.ID}}\n" + + "TASK_NAME={{.Task.Name}}\n" + + "NODE_ID={{.Node.ID}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SERVICE_ID=serviceID\n" + + "SERVICE_NAME=serviceName\n" + + "TASK_ID=taskID\n" + + "TASK_NAME=serviceName.10.taskID\n" + + "NODE_ID=nodeID\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret, by source", + configSpec: api.ConfigSpec{ + Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL=mysecret\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "referencedsecret", + SecretName: "referencedsecretname", + }, + }, + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret, by target", + configSpec: api.ConfigSpec{ + Data: []byte("SECRET_VAL={{Secret \"referencedsecrettarget\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL=mysecret\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Secrets: []*api.SecretReference{ + { + SecretID: "referencedsecret", + SecretName: "referencedsecretname", + Target: &api.SecretReference_File{ + File: &api.FileTarget{ + Name: "referencedsecrettarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, + }, + }, + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config, by source", + configSpec: api.ConfigSpec{ + Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL=myconfig\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + { + ConfigID: "referencedconfig", + ConfigName: "referencedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config, by target", + configSpec: api.ConfigSpec{ + Data: []byte("CONFIG_VAL={{Config \"referencedconfigtarget\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL=myconfig\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + { + ConfigID: "referencedconfig", + ConfigName: "referencedconfigname", + Target: &api.ConfigReference_File{ + File: &api.FileTarget{ + Name: "referencedconfigtarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of secret not available to task", + configSpec: api.ConfigSpec{ + Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test expansion of config not available to task", + configSpec: api.ConfigSpec{ + Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test that expansion of the same config avoids recursion", + configSpec: api.ConfigSpec{ + Data: []byte("CONFIG_VAL={{ConfigBySource \"templatedconfigname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "CONFIG_VAL=CONFIG_VAL={{ConfigBySource \"templatedconfigname\"}}\n\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + }, + }, + } + }), + }, + { + desc: "Test env", + configSpec: api.ConfigSpec{ + Data: []byte("ENV VALUE={{Env \"foo\"}}\n" + + "DOES NOT EXIST={{Env \"badname\"}}\n"), + Templating: api.Templating_GO_TEMPLATE, + }, + expected: "ENV VALUE=bar\n" + + "DOES NOT EXIST=\n", + task: modifyTask(func(t *api.Task) { + t.Spec = api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{ + Configs: []*api.ConfigReference{ + { + ConfigID: "templatedconfig", + ConfigName: "templatedconfigname", + }, + }, + Env: []string{"foo=bar"}, + }, + }, + } + }), + }, + } + + for _, testCase := range testCases { + templatedConfig.Spec = testCase.configSpec + + dependencyManager := agent.NewDependencyManager() + dependencyManager.Configs().Add(*templatedConfig, *referencedConfig) + dependencyManager.Secrets().Add(*referencedSecret) + + templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task) + expandedConfig := templatedDependencies.Configs().Get("templatedconfig") + + require.NotNil(t, expandedConfig) + assert.Equal(t, testCase.expected, string(expandedConfig.Spec.Data), testCase.desc) + } +} diff --git a/template/template.go b/template/template.go index 9f1517c662..fc375b819c 100644 --- a/template/template.go +++ b/template/template.go @@ -13,6 +13,10 @@ var funcMap = template.FuncMap{ }, } -func newTemplate(s string) (*template.Template, error) { - return template.New("expansion").Option("missingkey=error").Funcs(funcMap).Parse(s) +func newTemplate(s string, extraFuncs template.FuncMap) (*template.Template, error) { + tmpl := template.New("expansion").Option("missingkey=error").Funcs(funcMap) + if len(extraFuncs) != 0 { + tmpl = tmpl.Funcs(extraFuncs) + } + return tmpl.Parse(s) } From 53a023f7991be2b113eb96c3254e2033a6008e1c Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Mon, 1 May 2017 18:47:35 -0700 Subject: [PATCH 02/12] Change SecretGetter's Get to return an error This allows a templating error to fail a task. Signed-off-by: Aaron Lehmann --- agent/configs/configs.go | 11 ++++----- agent/exec/executor.go | 4 ++-- agent/secrets/secrets.go | 11 ++++----- agent/worker_test.go | 24 +++++++++++++++----- template/context.go | 32 +++++++++++++-------------- template/getter.go | 36 ++++++++++++++---------------- template/getter_test.go | 48 +++++++++++++++++++++++++--------------- 7 files changed, 95 insertions(+), 71 deletions(-) diff --git a/agent/configs/configs.go b/agent/configs/configs.go index f29e8afcba..ae5fc8c18c 100644 --- a/agent/configs/configs.go +++ b/agent/configs/configs.go @@ -1,6 +1,7 @@ package configs import ( + "fmt" "sync" "github.com/docker/swarmkit/agent/exec" @@ -22,13 +23,13 @@ func NewManager() exec.ConfigsManager { } // Get returns a config by ID. If the config doesn't exist, returns nil. -func (r *configs) Get(configID string) *api.Config { +func (r *configs) Get(configID string) (*api.Config, error) { r.mu.RLock() defer r.mu.RUnlock() if r, ok := r.m[configID]; ok { - return r + return r, nil } - return nil + return nil, fmt.Errorf("config %s not found", configID) } // Add adds one or more configs to the config map. @@ -63,9 +64,9 @@ type taskRestrictedConfigsProvider struct { configIDs map[string]struct{} // allow list of config ids } -func (sp *taskRestrictedConfigsProvider) Get(configID string) *api.Config { +func (sp *taskRestrictedConfigsProvider) Get(configID string) (*api.Config, error) { if _, ok := sp.configIDs[configID]; !ok { - return nil + return nil, fmt.Errorf("task not authorized to access config %s", configID) } return sp.configs.Get(configID) diff --git a/agent/exec/executor.go b/agent/exec/executor.go index 12bf29b4d2..8c3fd03506 100644 --- a/agent/exec/executor.go +++ b/agent/exec/executor.go @@ -52,7 +52,7 @@ type DependencyGetter interface { type SecretGetter interface { // Get returns the the secret with a specific secret ID, if available. // When the secret is not available, the return will be nil. - Get(secretID string) *api.Secret + Get(secretID string) (*api.Secret, error) } // SecretsManager is the interface for secret storage and updates. @@ -68,7 +68,7 @@ type SecretsManager interface { type ConfigGetter interface { // Get returns the the config with a specific config ID, if available. // When the config is not available, the return will be nil. - Get(configID string) *api.Config + Get(configID string) (*api.Config, error) } // ConfigsManager is the interface for config storage and updates. diff --git a/agent/secrets/secrets.go b/agent/secrets/secrets.go index 2f68fa20f3..233101d0fb 100644 --- a/agent/secrets/secrets.go +++ b/agent/secrets/secrets.go @@ -1,6 +1,7 @@ package secrets import ( + "fmt" "sync" "github.com/docker/swarmkit/agent/exec" @@ -22,13 +23,13 @@ func NewManager() exec.SecretsManager { } // Get returns a secret by ID. If the secret doesn't exist, returns nil. -func (s *secrets) Get(secretID string) *api.Secret { +func (s *secrets) Get(secretID string) (*api.Secret, error) { s.mu.RLock() defer s.mu.RUnlock() if s, ok := s.m[secretID]; ok { - return s + return s, nil } - return nil + return nil, fmt.Errorf("secret %s not found", secretID) } // Add adds one or more secrets to the secret map. @@ -63,9 +64,9 @@ type taskRestrictedSecretsProvider struct { secretIDs map[string]struct{} // allow list of secret ids } -func (sp *taskRestrictedSecretsProvider) Get(secretID string) *api.Secret { +func (sp *taskRestrictedSecretsProvider) Get(secretID string) (*api.Secret, error) { if _, ok := sp.secretIDs[secretID]; !ok { - return nil + return nil, fmt.Errorf("task not authorized to access secret %s", secretID) } return sp.secrets.Get(secretID) diff --git a/agent/worker_test.go b/agent/worker_test.go index 3a2b87e69c..38a07991f5 100644 --- a/agent/worker_test.go +++ b/agent/worker_test.go @@ -186,10 +186,14 @@ func TestWorkerAssign(t *testing.T) { assert.Equal(t, testcase.expectedTasks, tasks) assert.Equal(t, testcase.expectedAssigned, assigned) for _, secret := range testcase.expectedSecrets { - assert.NotNil(t, executor.Secrets().Get(secret.ID)) + secret, err := executor.Secrets().Get(secret.ID) + assert.NoError(t, err) + assert.NotNil(t, secret) } for _, config := range testcase.expectedConfigs { - assert.NotNil(t, executor.Configs().Get(config.ID)) + config, err := executor.Configs().Get(config.ID) + assert.NoError(t, err) + assert.NotNil(t, config) } } } @@ -280,10 +284,14 @@ func TestWorkerWait(t *testing.T) { assert.Equal(t, expectedTasks, tasks) assert.Equal(t, expectedAssigned, assigned) for _, secret := range expectedSecrets { - assert.NotNil(t, executor.Secrets().Get(secret.ID)) + secret, err := executor.Secrets().Get(secret.ID) + assert.NoError(t, err) + assert.NotNil(t, secret) } for _, config := range expectedConfigs { - assert.NotNil(t, executor.Configs().Get(config.ID)) + config, err := executor.Configs().Get(config.ID) + assert.NoError(t, err) + assert.NotNil(t, config) } err := worker.Assign(ctx, nil) @@ -573,10 +581,14 @@ func TestWorkerUpdate(t *testing.T) { assert.Equal(t, testcase.expectedTasks, tasks) assert.Equal(t, testcase.expectedAssigned, assigned) for _, secret := range testcase.expectedSecrets { - assert.NotNil(t, executor.Secrets().Get(secret.ID)) + secret, err := executor.Secrets().Get(secret.ID) + assert.NoError(t, err) + assert.NotNil(t, secret) } for _, config := range testcase.expectedConfigs { - assert.NotNil(t, executor.Configs().Get(config.ID)) + config, err := executor.Configs().Get(config.ID) + assert.NoError(t, err) + assert.NotNil(t, config) } } } diff --git a/template/context.go b/template/context.go index 4afa1fc8c5..6b320e7eba 100644 --- a/template/context.go +++ b/template/context.go @@ -104,11 +104,11 @@ func (ctx PayloadContext) secretGetterBySource(source string) (string, error) { for _, secretRef := range container.Secrets { if secretRef.SecretName == source { - secret := ctx.restrictedSecrets.Get(secretRef.SecretID) - if secret != nil { - return string(secret.Spec.Data), nil + secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) + if err != nil { + return "", err } - break + return string(secret.Spec.Data), nil } } @@ -128,11 +128,11 @@ func (ctx PayloadContext) secretGetterByTarget(target string) (string, error) { for _, secretRef := range container.Secrets { file := secretRef.GetFile() if file != nil && file.Name == target { - secret := ctx.restrictedSecrets.Get(secretRef.SecretID) - if secret != nil { - return string(secret.Spec.Data), nil + secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) + if err != nil { + return "", err } - break + return string(secret.Spec.Data), nil } } @@ -151,11 +151,11 @@ func (ctx PayloadContext) configGetterBySource(source string) (string, error) { for _, configRef := range container.Configs { if configRef.ConfigName == source { - config := ctx.restrictedConfigs.Get(configRef.ConfigID) - if config != nil { - return string(config.Spec.Data), nil + config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) + if err != nil { + return "", err } - break + return string(config.Spec.Data), nil } } @@ -175,11 +175,11 @@ func (ctx PayloadContext) configGetterByTarget(target string) (string, error) { for _, configRef := range container.Configs { file := configRef.GetFile() if file != nil && file.Name == target { - config := ctx.restrictedConfigs.Get(configRef.ConfigID) - if config != nil { - return string(config.Spec.Data), nil + config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) + if err != nil { + return "", err } - break + return string(config.Spec.Data), nil } } diff --git a/template/getter.go b/template/getter.go index 9b0e377477..f06c438c25 100644 --- a/template/getter.go +++ b/template/getter.go @@ -3,7 +3,7 @@ package template import ( "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" - "github.com/docker/swarmkit/log" + "github.com/pkg/errors" ) type templatedSecretGetter struct { @@ -16,30 +16,29 @@ func NewTemplatedSecretGetter(dependencies exec.DependencyGetter, t *api.Task) e return templatedSecretGetter{dependencies: dependencies, t: t} } -func (t templatedSecretGetter) Get(secretID string) *api.Secret { +func (t templatedSecretGetter) Get(secretID string) (*api.Secret, error) { if t.dependencies == nil { - return nil + return nil, errors.New("no secret provider available") } secrets := t.dependencies.Secrets() if secrets == nil { - return nil + return nil, errors.New("no secret provider available") } - secret := secrets.Get(secretID) - if secret == nil { - return nil + secret, err := secrets.Get(secretID) + if err != nil { + return secret, err } newSpec, err := ExpandSecretSpec(secret, t.t, t.dependencies) if err != nil { - log.L.WithError(err).Error("failed to expand templated secret") - return secret + return secret, errors.Wrapf(err, "failed to expand templated secret %s", secretID) } secretCopy := *secret secretCopy.Spec = *newSpec - return &secretCopy + return &secretCopy, nil } type templatedConfigGetter struct { @@ -52,30 +51,29 @@ func NewTemplatedConfigGetter(dependencies exec.DependencyGetter, t *api.Task) e return templatedConfigGetter{dependencies: dependencies, t: t} } -func (t templatedConfigGetter) Get(configID string) *api.Config { +func (t templatedConfigGetter) Get(configID string) (*api.Config, error) { if t.dependencies == nil { - return nil + return nil, errors.New("no config provider available") } configs := t.dependencies.Configs() if configs == nil { - return nil + return nil, errors.New("no config provider available") } - config := configs.Get(configID) - if config == nil { - return nil + config, err := configs.Get(configID) + if err != nil { + return config, err } newSpec, err := ExpandConfigSpec(config, t.t, t.dependencies) if err != nil { - log.L.WithError(err).Error("failed to expand templated config") - return config + return config, errors.Wrapf(err, "failed to expand templated config %s", configID) } configCopy := *config configCopy.Spec = *newSpec - return &configCopy + return &configCopy, nil } type templatedDependencyGetter struct { diff --git a/template/getter_test.go b/template/getter_test.go index e0af65e1a4..7a9a070916 100644 --- a/template/getter_test.go +++ b/template/getter_test.go @@ -28,10 +28,11 @@ func TestTemplatedSecret(t *testing.T) { } type testCase struct { - desc string - secretSpec api.SecretSpec - task *api.Task - expected string + desc string + secretSpec api.SecretSpec + task *api.Task + expected string + expectedErr string } testCases := []testCase{ @@ -195,7 +196,7 @@ func TestTemplatedSecret(t *testing.T) { Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n", + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling SecretBySource: secret source referencedsecretname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -217,7 +218,7 @@ func TestTemplatedSecret(t *testing.T) { Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n", + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling ConfigBySource: config source referencedconfigname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -290,10 +291,15 @@ func TestTemplatedSecret(t *testing.T) { dependencyManager.Configs().Add(*referencedConfig) templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task) - expandedSecret := templatedDependencies.Secrets().Get("templatedsecret") + expandedSecret, err := templatedDependencies.Secrets().Get("templatedsecret") - require.NotNil(t, expandedSecret) - assert.Equal(t, testCase.expected, string(expandedSecret.Spec.Data), testCase.desc) + if testCase.expectedErr != "" { + assert.EqualError(t, err, testCase.expectedErr) + } else { + assert.NoError(t, err) + require.NotNil(t, expandedSecret) + assert.Equal(t, testCase.expected, string(expandedSecret.Spec.Data), testCase.desc) + } } } @@ -316,10 +322,11 @@ func TestTemplatedConfig(t *testing.T) { } type testCase struct { - desc string - configSpec api.ConfigSpec - task *api.Task - expected string + desc string + configSpec api.ConfigSpec + task *api.Task + expected string + expectedErr string } testCases := []testCase{ @@ -483,7 +490,7 @@ func TestTemplatedConfig(t *testing.T) { Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n", + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling SecretBySource: secret source referencedsecretname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -505,7 +512,7 @@ func TestTemplatedConfig(t *testing.T) { Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n", + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling ConfigBySource: config source referencedconfigname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -578,9 +585,14 @@ func TestTemplatedConfig(t *testing.T) { dependencyManager.Secrets().Add(*referencedSecret) templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task) - expandedConfig := templatedDependencies.Configs().Get("templatedconfig") + expandedConfig, err := templatedDependencies.Configs().Get("templatedconfig") - require.NotNil(t, expandedConfig) - assert.Equal(t, testCase.expected, string(expandedConfig.Spec.Data), testCase.desc) + if testCase.expectedErr != "" { + assert.EqualError(t, err, testCase.expectedErr) + } else { + assert.NoError(t, err) + require.NotNil(t, expandedConfig) + assert.Equal(t, testCase.expected, string(expandedConfig.Spec.Data), testCase.desc) + } } } From 907cf9f81da9260057cfd3ea3e243bd3752ceb88 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Mon, 1 May 2017 19:18:32 -0700 Subject: [PATCH 03/12] Rename template functions Rename Secret, Config, and Env to lowercase variants. Replace SecretBySource/ConfigBySource with optional bysource=true arguments to "secret" and "config". Signed-off-by: Aaron Lehmann --- template/context.go | 98 +++++++++++++++++++++++------------------ template/getter_test.go | 48 ++++++++++---------- 2 files changed, 79 insertions(+), 67 deletions(-) diff --git a/template/context.go b/template/context.go index 6b320e7eba..82f28441ae 100644 --- a/template/context.go +++ b/template/context.go @@ -92,7 +92,7 @@ type PayloadContext struct { restrictedConfigs exec.ConfigGetter } -func (ctx PayloadContext) secretGetterBySource(source string) (string, error) { +func (ctx PayloadContext) secretGetter(sourceOrTarget string, opts ...string) (string, error) { if ctx.restrictedSecrets == nil { return "", errors.New("secrets unavailable") } @@ -102,32 +102,39 @@ func (ctx PayloadContext) secretGetterBySource(source string) (string, error) { return "", errors.New("task is not a container") } - for _, secretRef := range container.Secrets { - if secretRef.SecretName == source { - secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) - if err != nil { - return "", err - } - return string(secret.Spec.Data), nil - } + if len(opts) > 1 { + return "", errors.New("too many arguments to secret") } - return "", errors.Errorf("secret source %s not found", source) -} + bySource := false -func (ctx PayloadContext) secretGetterByTarget(target string) (string, error) { - if ctx.restrictedSecrets == nil { - return "", errors.New("secrets unavailable") + if len(opts) == 1 { + switch opts[0] { + case "bysource=true": + bySource = true + case "bytarget=true": + default: + return "", errors.Errorf("unrecognized secret argument %q", opts[0]) + } } - container := ctx.t.Spec.GetContainer() - if container == nil { - return "", errors.New("task is not a container") + if bySource { + for _, secretRef := range container.Secrets { + if secretRef.SecretName == sourceOrTarget { + secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) + if err != nil { + return "", err + } + return string(secret.Spec.Data), nil + } + } + + return "", errors.Errorf("secret source %s not found", sourceOrTarget) } for _, secretRef := range container.Secrets { file := secretRef.GetFile() - if file != nil && file.Name == target { + if file != nil && file.Name == sourceOrTarget { secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) if err != nil { return "", err @@ -136,10 +143,10 @@ func (ctx PayloadContext) secretGetterByTarget(target string) (string, error) { } } - return "", errors.Errorf("secret target %s not found", target) + return "", errors.Errorf("secret target %s not found", sourceOrTarget) } -func (ctx PayloadContext) configGetterBySource(source string) (string, error) { +func (ctx PayloadContext) configGetter(sourceOrTarget string, opts ...string) (string, error) { if ctx.restrictedConfigs == nil { return "", errors.New("configs unavailable") } @@ -149,32 +156,39 @@ func (ctx PayloadContext) configGetterBySource(source string) (string, error) { return "", errors.New("task is not a container") } - for _, configRef := range container.Configs { - if configRef.ConfigName == source { - config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) - if err != nil { - return "", err - } - return string(config.Spec.Data), nil - } + if len(opts) > 1 { + return "", errors.New("too many arguments to secret") } - return "", errors.Errorf("config source %s not found", source) -} + bySource := false -func (ctx PayloadContext) configGetterByTarget(target string) (string, error) { - if ctx.restrictedConfigs == nil { - return "", errors.New("configs unvailable") + if len(opts) == 1 { + switch opts[0] { + case "bysource=true": + bySource = true + case "bytarget=true": + default: + return "", errors.Errorf("unrecognized secret argument %q", opts[0]) + } } - container := ctx.t.Spec.GetContainer() - if container == nil { - return "", errors.New("task is not a container") + if bySource { + for _, configRef := range container.Configs { + if configRef.ConfigName == sourceOrTarget { + config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) + if err != nil { + return "", err + } + return string(config.Spec.Data), nil + } + } + + return "", errors.Errorf("config source %s not found", sourceOrTarget) } for _, configRef := range container.Configs { file := configRef.GetFile() - if file != nil && file.Name == target { + if file != nil && file.Name == sourceOrTarget { config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) if err != nil { return "", err @@ -183,7 +197,7 @@ func (ctx PayloadContext) configGetterByTarget(target string) (string, error) { } } - return "", errors.Errorf("config target %s not found", target) + return "", errors.Errorf("config target %s not found", sourceOrTarget) } func (ctx PayloadContext) envGetter(variable string) (string, error) { @@ -219,11 +233,9 @@ func NewPayloadContextFromTask(t *api.Task, dependencies exec.DependencyGetter) // the context. func (ctx *PayloadContext) Expand(s string) (string, error) { funcMap := template.FuncMap{ - "SecretBySource": ctx.secretGetterBySource, - "Secret": ctx.secretGetterByTarget, - "ConfigBySource": ctx.configGetterBySource, - "Config": ctx.configGetterByTarget, - "Env": ctx.envGetter, + "secret": ctx.secretGetter, + "config": ctx.configGetter, + "env": ctx.envGetter, } tmpl, err := newTemplate(s, funcMap) diff --git a/template/getter_test.go b/template/getter_test.go index 7a9a070916..83001f46d8 100644 --- a/template/getter_test.go +++ b/template/getter_test.go @@ -69,7 +69,7 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of secret, by source", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "SECRET_VAL=mysecret\n", @@ -95,7 +95,7 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of secret, by target", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{Secret \"referencedsecrettarget\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "SECRET_VAL=mysecret\n", @@ -129,7 +129,7 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of config, by source", secretSpec: api.SecretSpec{ - Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "CONFIG_VAL=myconfig\n", @@ -157,7 +157,7 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of config, by target", secretSpec: api.SecretSpec{ - Data: []byte("CONFIG_VAL={{Config \"referencedconfigtarget\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "CONFIG_VAL=myconfig\n", @@ -193,10 +193,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of secret not available to task", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling SecretBySource: secret source referencedsecretname not found`, + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling secret: secret source referencedsecretname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -215,10 +215,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of config not available to task", secretSpec: api.SecretSpec{ - Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling ConfigBySource: config source referencedconfigname not found`, + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling config: config source referencedconfigname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -237,10 +237,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test that expansion of the same secret avoids recursion", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{SecretBySource \"templatedsecretname\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"templatedsecretname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "SECRET_VAL=SECRET_VAL={{SecretBySource \"templatedsecretname\"}}\n\n", + expected: "SECRET_VAL=SECRET_VAL={{secret \"templatedsecretname\" \"bysource=true\"}}\n\n", task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -259,8 +259,8 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test env", secretSpec: api.SecretSpec{ - Data: []byte("ENV VALUE={{Env \"foo\"}}\n" + - "DOES NOT EXIST={{Env \"badname\"}}\n"), + Data: []byte("ENV VALUE={{env \"foo\"}}\n" + + "DOES NOT EXIST={{env \"badname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "ENV VALUE=bar\n" + @@ -363,7 +363,7 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of secret, by source", configSpec: api.ConfigSpec{ - Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "SECRET_VAL=mysecret\n", @@ -391,7 +391,7 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of secret, by target", configSpec: api.ConfigSpec{ - Data: []byte("SECRET_VAL={{Secret \"referencedsecrettarget\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "SECRET_VAL=mysecret\n", @@ -427,7 +427,7 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of config, by source", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "CONFIG_VAL=myconfig\n", @@ -453,7 +453,7 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of config, by target", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{Config \"referencedconfigtarget\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "CONFIG_VAL=myconfig\n", @@ -487,10 +487,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of secret not available to task", configSpec: api.ConfigSpec{ - Data: []byte("SECRET_VAL={{SecretBySource \"referencedsecretname\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling SecretBySource: secret source referencedsecretname not found`, + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling secret: secret source referencedsecretname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -509,10 +509,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of config not available to task", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{ConfigBySource \"referencedconfigname\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling ConfigBySource: config source referencedconfigname not found`, + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling config: config source referencedconfigname not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -531,10 +531,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test that expansion of the same config avoids recursion", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{ConfigBySource \"templatedconfigname\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"templatedconfigname\" \"bysource=true\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "CONFIG_VAL=CONFIG_VAL={{ConfigBySource \"templatedconfigname\"}}\n\n", + expected: "CONFIG_VAL=CONFIG_VAL={{config \"templatedconfigname\" \"bysource=true\"}}\n\n", task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -553,8 +553,8 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test env", configSpec: api.ConfigSpec{ - Data: []byte("ENV VALUE={{Env \"foo\"}}\n" + - "DOES NOT EXIST={{Env \"badname\"}}\n"), + Data: []byte("ENV VALUE={{env \"foo\"}}\n" + + "DOES NOT EXIST={{env \"badname\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, expected: "ENV VALUE=bar\n" + From 0534c834c38322cf23d50a8d01473a2672687c83 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Wed, 3 May 2017 14:13:57 -0700 Subject: [PATCH 04/12] templating: Remove bysource option Signed-off-by: Aaron Lehmann --- template/context.go | 72 ++----------------- template/getter_test.go | 148 ++++++++-------------------------------- 2 files changed, 34 insertions(+), 186 deletions(-) diff --git a/template/context.go b/template/context.go index 82f28441ae..d26e155be4 100644 --- a/template/context.go +++ b/template/context.go @@ -92,7 +92,7 @@ type PayloadContext struct { restrictedConfigs exec.ConfigGetter } -func (ctx PayloadContext) secretGetter(sourceOrTarget string, opts ...string) (string, error) { +func (ctx PayloadContext) secretGetter(target string) (string, error) { if ctx.restrictedSecrets == nil { return "", errors.New("secrets unavailable") } @@ -102,39 +102,9 @@ func (ctx PayloadContext) secretGetter(sourceOrTarget string, opts ...string) (s return "", errors.New("task is not a container") } - if len(opts) > 1 { - return "", errors.New("too many arguments to secret") - } - - bySource := false - - if len(opts) == 1 { - switch opts[0] { - case "bysource=true": - bySource = true - case "bytarget=true": - default: - return "", errors.Errorf("unrecognized secret argument %q", opts[0]) - } - } - - if bySource { - for _, secretRef := range container.Secrets { - if secretRef.SecretName == sourceOrTarget { - secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) - if err != nil { - return "", err - } - return string(secret.Spec.Data), nil - } - } - - return "", errors.Errorf("secret source %s not found", sourceOrTarget) - } - for _, secretRef := range container.Secrets { file := secretRef.GetFile() - if file != nil && file.Name == sourceOrTarget { + if file != nil && file.Name == target { secret, err := ctx.restrictedSecrets.Get(secretRef.SecretID) if err != nil { return "", err @@ -143,10 +113,10 @@ func (ctx PayloadContext) secretGetter(sourceOrTarget string, opts ...string) (s } } - return "", errors.Errorf("secret target %s not found", sourceOrTarget) + return "", errors.Errorf("secret target %s not found", target) } -func (ctx PayloadContext) configGetter(sourceOrTarget string, opts ...string) (string, error) { +func (ctx PayloadContext) configGetter(target string) (string, error) { if ctx.restrictedConfigs == nil { return "", errors.New("configs unavailable") } @@ -156,39 +126,9 @@ func (ctx PayloadContext) configGetter(sourceOrTarget string, opts ...string) (s return "", errors.New("task is not a container") } - if len(opts) > 1 { - return "", errors.New("too many arguments to secret") - } - - bySource := false - - if len(opts) == 1 { - switch opts[0] { - case "bysource=true": - bySource = true - case "bytarget=true": - default: - return "", errors.Errorf("unrecognized secret argument %q", opts[0]) - } - } - - if bySource { - for _, configRef := range container.Configs { - if configRef.ConfigName == sourceOrTarget { - config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) - if err != nil { - return "", err - } - return string(config.Spec.Data), nil - } - } - - return "", errors.Errorf("config source %s not found", sourceOrTarget) - } - for _, configRef := range container.Configs { file := configRef.GetFile() - if file != nil && file.Name == sourceOrTarget { + if file != nil && file.Name == target { config, err := ctx.restrictedConfigs.Get(configRef.ConfigID) if err != nil { return "", err @@ -197,7 +137,7 @@ func (ctx PayloadContext) configGetter(sourceOrTarget string, opts ...string) (s } } - return "", errors.Errorf("config target %s not found", sourceOrTarget) + return "", errors.Errorf("config target %s not found", target) } func (ctx PayloadContext) envGetter(variable string) (string, error) { diff --git a/template/getter_test.go b/template/getter_test.go index 83001f46d8..deadbf3f9d 100644 --- a/template/getter_test.go +++ b/template/getter_test.go @@ -66,32 +66,6 @@ func TestTemplatedSecret(t *testing.T) { } }), }, - { - desc: "Test expansion of secret, by source", - secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, - }, - expected: "SECRET_VAL=mysecret\n", - task: modifyTask(func(t *api.Task) { - t.Spec = api.TaskSpec{ - Runtime: &api.TaskSpec_Container{ - Container: &api.ContainerSpec{ - Secrets: []*api.SecretReference{ - { - SecretID: "templatedsecret", - SecretName: "templatedsecretname", - }, - { - SecretID: "referencedsecret", - SecretName: "referencedsecretname", - }, - }, - }, - }, - } - }), - }, { desc: "Test expansion of secret, by target", secretSpec: api.SecretSpec{ @@ -126,34 +100,6 @@ func TestTemplatedSecret(t *testing.T) { } }), }, - { - desc: "Test expansion of config, by source", - secretSpec: api.SecretSpec{ - Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, - }, - expected: "CONFIG_VAL=myconfig\n", - task: modifyTask(func(t *api.Task) { - t.Spec = api.TaskSpec{ - Runtime: &api.TaskSpec_Container{ - Container: &api.ContainerSpec{ - Secrets: []*api.SecretReference{ - { - SecretID: "templatedsecret", - SecretName: "templatedsecretname", - }, - }, - Configs: []*api.ConfigReference{ - { - ConfigID: "referencedconfig", - ConfigName: "referencedconfigname", - }, - }, - }, - }, - } - }), - }, { desc: "Test expansion of config, by target", secretSpec: api.SecretSpec{ @@ -193,10 +139,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of secret not available to task", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling secret: secret source referencedsecretname not found`, + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling secret: secret target unknowntarget not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -215,10 +161,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test expansion of config not available to task", secretSpec: api.SecretSpec{ - Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling config: config source referencedconfigname not found`, + expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling config: config target unknowntarget not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -237,10 +183,10 @@ func TestTemplatedSecret(t *testing.T) { { desc: "Test that expansion of the same secret avoids recursion", secretSpec: api.SecretSpec{ - Data: []byte("SECRET_VAL={{secret \"templatedsecretname\" \"bysource=true\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"templatedsecrettarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "SECRET_VAL=SECRET_VAL={{secret \"templatedsecretname\" \"bysource=true\"}}\n\n", + expected: "SECRET_VAL=SECRET_VAL={{secret \"templatedsecrettarget\"}}\n\n", task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -249,6 +195,14 @@ func TestTemplatedSecret(t *testing.T) { { SecretID: "templatedsecret", SecretName: "templatedsecretname", + Target: &api.SecretReference_File{ + File: &api.FileTarget{ + Name: "templatedsecrettarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, }, }, }, @@ -360,34 +314,6 @@ func TestTemplatedConfig(t *testing.T) { } }), }, - { - desc: "Test expansion of secret, by source", - configSpec: api.ConfigSpec{ - Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, - }, - expected: "SECRET_VAL=mysecret\n", - task: modifyTask(func(t *api.Task) { - t.Spec = api.TaskSpec{ - Runtime: &api.TaskSpec_Container{ - Container: &api.ContainerSpec{ - Secrets: []*api.SecretReference{ - { - SecretID: "referencedsecret", - SecretName: "referencedsecretname", - }, - }, - Configs: []*api.ConfigReference{ - { - ConfigID: "templatedconfig", - ConfigName: "templatedconfigname", - }, - }, - }, - }, - } - }), - }, { desc: "Test expansion of secret, by target", configSpec: api.ConfigSpec{ @@ -424,32 +350,6 @@ func TestTemplatedConfig(t *testing.T) { } }), }, - { - desc: "Test expansion of config, by source", - configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, - }, - expected: "CONFIG_VAL=myconfig\n", - task: modifyTask(func(t *api.Task) { - t.Spec = api.TaskSpec{ - Runtime: &api.TaskSpec_Container{ - Container: &api.ContainerSpec{ - Configs: []*api.ConfigReference{ - { - ConfigID: "templatedconfig", - ConfigName: "templatedconfigname", - }, - { - ConfigID: "referencedconfig", - ConfigName: "referencedconfigname", - }, - }, - }, - }, - } - }), - }, { desc: "Test expansion of config, by target", configSpec: api.ConfigSpec{ @@ -487,10 +387,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of secret not available to task", configSpec: api.ConfigSpec{ - Data: []byte("SECRET_VAL={{secret \"referencedsecretname\" \"bysource=true\"}}\n"), + Data: []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling secret: secret source referencedsecretname not found`, + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling secret: secret target unknowntarget not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -509,10 +409,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test expansion of config not available to task", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{config \"referencedconfigname\" \"bysource=true\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling config: config source referencedconfigname not found`, + expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling config: config target unknowntarget not found`, task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -531,10 +431,10 @@ func TestTemplatedConfig(t *testing.T) { { desc: "Test that expansion of the same config avoids recursion", configSpec: api.ConfigSpec{ - Data: []byte("CONFIG_VAL={{config \"templatedconfigname\" \"bysource=true\"}}\n"), + Data: []byte("CONFIG_VAL={{config \"templatedconfigtarget\"}}\n"), Templating: api.Templating_GO_TEMPLATE, }, - expected: "CONFIG_VAL=CONFIG_VAL={{config \"templatedconfigname\" \"bysource=true\"}}\n\n", + expected: "CONFIG_VAL=CONFIG_VAL={{config \"templatedconfigtarget\"}}\n\n", task: modifyTask(func(t *api.Task) { t.Spec = api.TaskSpec{ Runtime: &api.TaskSpec_Container{ @@ -543,6 +443,14 @@ func TestTemplatedConfig(t *testing.T) { { ConfigID: "templatedconfig", ConfigName: "templatedconfigname", + Target: &api.ConfigReference_File{ + File: &api.FileTarget{ + Name: "templatedconfigtarget", + UID: "0", + GID: "0", + Mode: 0666, + }, + }, }, }, }, From ed762759469ab692388fa5dacd669a39641de6e5 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Wed, 10 May 2017 17:47:12 -0700 Subject: [PATCH 05/12] Change Templating to use the Driver type Signed-off-by: Aaron Lehmann --- api/specs.pb.go | 366 +++++++++++++++++++++------------------- api/specs.proto | 23 ++- template/expand.go | 18 +- template/getter_test.go | 28 +-- 4 files changed, 230 insertions(+), 205 deletions(-) diff --git a/api/specs.pb.go b/api/specs.pb.go index 04ddfaed10..8578cf3849 100644 --- a/api/specs.pb.go +++ b/api/specs.pb.go @@ -24,29 +24,6 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf -// Templating controls whether the payload of a config or secret is evaluated -// as a template. -type Templating int32 - -const ( - Templating_NONE Templating = 0 - Templating_GO_TEMPLATE Templating = 1 -) - -var Templating_name = map[int32]string{ - 0: "NONE", - 1: "GO_TEMPLATE", -} -var Templating_value = map[string]int32{ - "NONE": 0, - "GO_TEMPLATE": 1, -} - -func (x Templating) String() string { - return proto.EnumName(Templating_name, int32(x)) -} -func (Templating) EnumDescriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0} } - type NodeSpec_Membership int32 const ( @@ -784,8 +761,11 @@ type SecretSpec struct { // Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes) Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` // Templating controls whether and how to evaluate the secret payload as - // a template. - Templating Templating `protobuf:"varint,3,opt,name=templating,proto3,enum=docker.swarmkit.v1.Templating" json:"templating,omitempty"` + // a template. If it is not set, no templating is used. + // + // The currently recognized values are: + // - golang: Go templating + Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"` } func (m *SecretSpec) Reset() { *m = SecretSpec{} } @@ -799,9 +779,12 @@ type ConfigSpec struct { // TODO(aaronl): Do we want to revise this to include multiple payloads in a single // ConfigSpec? Define this to be a tar? etc... Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - // Templating controls whether and how to evaluate the config payload as - // a template. - Templating Templating `protobuf:"varint,3,opt,name=templating,proto3,enum=docker.swarmkit.v1.Templating" json:"templating,omitempty"` + // Templating controls whether and how to evaluate the secret payload as + // a template. If it is not set, no templating is used. + // + // The currently recognized values are: + // - golang: Go templating + Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"` } func (m *ConfigSpec) Reset() { *m = ConfigSpec{} } @@ -824,7 +807,6 @@ func init() { proto.RegisterType((*ClusterSpec)(nil), "docker.swarmkit.v1.ClusterSpec") proto.RegisterType((*SecretSpec)(nil), "docker.swarmkit.v1.SecretSpec") proto.RegisterType((*ConfigSpec)(nil), "docker.swarmkit.v1.ConfigSpec") - proto.RegisterEnum("docker.swarmkit.v1.Templating", Templating_name, Templating_value) proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Membership", NodeSpec_Membership_name, NodeSpec_Membership_value) proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Availability", NodeSpec_Availability_name, NodeSpec_Availability_value) proto.RegisterEnum("docker.swarmkit.v1.EndpointSpec_ResolutionMode", EndpointSpec_ResolutionMode_name, EndpointSpec_ResolutionMode_value) @@ -1254,6 +1236,10 @@ func (m *SecretSpec) CopyFrom(src interface{}) { m.Data = make([]byte, len(o.Data)) copy(m.Data, o.Data) } + if o.Templating != nil { + m.Templating = &Driver{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating) + } } func (m *ConfigSpec) Copy() *ConfigSpec { @@ -1274,6 +1260,10 @@ func (m *ConfigSpec) CopyFrom(src interface{}) { m.Data = make([]byte, len(o.Data)) copy(m.Data, o.Data) } + if o.Templating != nil { + m.Templating = &Driver{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating) + } } func (m *NodeSpec) Marshal() (dAtA []byte, err error) { @@ -2257,10 +2247,15 @@ func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) i += copy(dAtA[i:], m.Data) } - if m.Templating != 0 { - dAtA[i] = 0x18 + if m.Templating != nil { + dAtA[i] = 0x1a i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Templating)) + i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) + n37, err := m.Templating.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n37 } return i, nil } @@ -2283,21 +2278,26 @@ func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n37, err := m.Annotations.MarshalTo(dAtA[i:]) + n38, err := m.Annotations.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n37 + i += n38 if len(m.Data) > 0 { dAtA[i] = 0x12 i++ i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) i += copy(dAtA[i:], m.Data) } - if m.Templating != 0 { - dAtA[i] = 0x18 + if m.Templating != nil { + dAtA[i] = 0x1a i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Templating)) + i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) + n39, err := m.Templating.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n39 } return i, nil } @@ -2725,8 +2725,9 @@ func (m *SecretSpec) Size() (n int) { if l > 0 { n += 1 + l + sovSpecs(uint64(l)) } - if m.Templating != 0 { - n += 1 + sovSpecs(uint64(m.Templating)) + if m.Templating != nil { + l = m.Templating.Size() + n += 1 + l + sovSpecs(uint64(l)) } return n } @@ -2740,8 +2741,9 @@ func (m *ConfigSpec) Size() (n int) { if l > 0 { n += 1 + l + sovSpecs(uint64(l)) } - if m.Templating != 0 { - n += 1 + sovSpecs(uint64(m.Templating)) + if m.Templating != nil { + l = m.Templating.Size() + n += 1 + l + sovSpecs(uint64(l)) } return n } @@ -3019,7 +3021,7 @@ func (this *SecretSpec) String() string { s := strings.Join([]string{`&SecretSpec{`, `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, - `Templating:` + fmt.Sprintf("%v", this.Templating) + `,`, + `Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`, `}`, }, "") return s @@ -3031,7 +3033,7 @@ func (this *ConfigSpec) String() string { s := strings.Join([]string{`&ConfigSpec{`, `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, - `Templating:` + fmt.Sprintf("%v", this.Templating) + `,`, + `Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`, `}`, }, "") return s @@ -5849,10 +5851,10 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: - if wireType != 0 { + if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType) } - m.Templating = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSpecs @@ -5862,11 +5864,25 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Templating |= (Templating(b) & 0x7F) << shift + msglen |= (int(b) & 0x7F) << shift if b < 0x80 { break } } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Templating == nil { + m.Templating = &Driver{} + } + if err := m.Templating.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -5979,10 +5995,10 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: - if wireType != 0 { + if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType) } - m.Templating = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSpecs @@ -5992,11 +6008,25 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Templating |= (Templating(b) & 0x7F) << shift + msglen |= (int(b) & 0x7F) << shift if b < 0x80 { break } } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Templating == nil { + m.Templating = &Driver{} + } + if err := m.Templating.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -6126,124 +6156,122 @@ var ( func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) } var fileDescriptorSpecs = []byte{ - // 1901 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcf, 0x73, 0x1b, 0x49, - 0x15, 0x96, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xd2, 0x24, 0x61, 0xac, 0xb0, 0xb2, 0xa2, 0x0d, - 0x59, 0xef, 0x6e, 0x21, 0x17, 0x86, 0x5a, 0xb2, 0x84, 0x05, 0x24, 0x4b, 0x38, 0xc6, 0x6b, 0x5b, - 0xd5, 0xf6, 0x06, 0x72, 0x52, 0xb5, 0x67, 0xda, 0xd2, 0x94, 0x47, 0xdd, 0x43, 0x4f, 0x8f, 0xb6, - 0x74, 0xe3, 0xb8, 0x95, 0x2b, 0x67, 0x17, 0x07, 0x4e, 0xfc, 0x27, 0x39, 0x52, 0x9c, 0x38, 0xb9, - 0x58, 0xff, 0x0b, 0xdc, 0xb8, 0x40, 0x75, 0x4f, 0x8f, 0x34, 0x4a, 0xc6, 0x9b, 0x54, 0x91, 0x03, - 0xb7, 0xee, 0xd7, 0xdf, 0xf7, 0xe6, 0xf5, 0xeb, 0xaf, 0xfb, 0x3d, 0x09, 0xac, 0x30, 0xa0, 0x4e, - 0xd8, 0x0e, 0x04, 0x97, 0x1c, 0x21, 0x97, 0x3b, 0x17, 0x54, 0xb4, 0xc3, 0xaf, 0x89, 0x98, 0x5c, - 0x78, 0xb2, 0x3d, 0xfd, 0x71, 0xdd, 0x92, 0xb3, 0x80, 0x1a, 0x40, 0xfd, 0xee, 0x88, 0x8f, 0xb8, - 0x1e, 0x6e, 0xab, 0x91, 0xb1, 0x36, 0x46, 0x9c, 0x8f, 0x7c, 0xba, 0xad, 0x67, 0x67, 0xd1, 0xf9, - 0xb6, 0x1b, 0x09, 0x22, 0x3d, 0xce, 0xcc, 0xfa, 0xc6, 0xeb, 0xeb, 0x84, 0xcd, 0xe2, 0xa5, 0xd6, - 0x65, 0x01, 0xca, 0x47, 0xdc, 0xa5, 0x27, 0x01, 0x75, 0xd0, 0x1e, 0x58, 0x84, 0x31, 0x2e, 0x35, - 0x37, 0xb4, 0xf3, 0xcd, 0xfc, 0x96, 0xb5, 0xb3, 0xd9, 0x7e, 0x33, 0xa8, 0x76, 0x67, 0x01, 0xeb, - 0x16, 0x5e, 0x5d, 0x6d, 0xe6, 0x70, 0x9a, 0x89, 0x7e, 0x05, 0x55, 0x97, 0x86, 0x9e, 0xa0, 0xee, - 0x50, 0x70, 0x9f, 0xda, 0x2b, 0xcd, 0xfc, 0xd6, 0xad, 0x9d, 0x1f, 0x64, 0x79, 0x52, 0x1f, 0xc7, - 0xdc, 0xa7, 0xd8, 0x32, 0x0c, 0x35, 0x41, 0x7b, 0x00, 0x13, 0x3a, 0x39, 0xa3, 0x22, 0x1c, 0x7b, - 0x81, 0xbd, 0xaa, 0xe9, 0x1f, 0xdd, 0x44, 0x57, 0xb1, 0xb7, 0x0f, 0xe7, 0x70, 0x9c, 0xa2, 0xa2, - 0x43, 0xa8, 0x92, 0x29, 0xf1, 0x7c, 0x72, 0xe6, 0xf9, 0x9e, 0x9c, 0xd9, 0x05, 0xed, 0xea, 0xe3, - 0xef, 0x74, 0xd5, 0x49, 0x11, 0xf0, 0x12, 0xbd, 0xe5, 0x02, 0x2c, 0x3e, 0x84, 0x1e, 0x43, 0x69, - 0xd0, 0x3f, 0xea, 0xed, 0x1f, 0xed, 0xd5, 0x72, 0xf5, 0x8d, 0x97, 0x97, 0xcd, 0x7b, 0xca, 0xc7, - 0x02, 0x30, 0xa0, 0xcc, 0xf5, 0xd8, 0x08, 0x6d, 0x41, 0xb9, 0xb3, 0xbb, 0xdb, 0x1f, 0x9c, 0xf6, - 0x7b, 0xb5, 0x7c, 0xbd, 0xfe, 0xf2, 0xb2, 0x79, 0x7f, 0x19, 0xd8, 0x71, 0x1c, 0x1a, 0x48, 0xea, - 0xd6, 0x0b, 0xdf, 0xfc, 0xa5, 0x91, 0x6b, 0x7d, 0x93, 0x87, 0x6a, 0x3a, 0x08, 0xf4, 0x18, 0x8a, - 0x9d, 0xdd, 0xd3, 0xfd, 0xe7, 0xfd, 0x5a, 0x6e, 0x41, 0x4f, 0x23, 0x3a, 0x8e, 0xf4, 0xa6, 0x14, - 0x3d, 0x82, 0xb5, 0x41, 0xe7, 0xab, 0x93, 0x7e, 0x2d, 0xbf, 0x08, 0x27, 0x0d, 0x1b, 0x90, 0x28, - 0xd4, 0xa8, 0x1e, 0xee, 0xec, 0x1f, 0xd5, 0x56, 0xb2, 0x51, 0x3d, 0x41, 0x3c, 0x66, 0x42, 0xf9, - 0x73, 0x01, 0xac, 0x13, 0x2a, 0xa6, 0x9e, 0xf3, 0x9e, 0x25, 0xf2, 0x19, 0x14, 0x24, 0x09, 0x2f, - 0xb4, 0x34, 0xac, 0x6c, 0x69, 0x9c, 0x92, 0xf0, 0x42, 0x7d, 0xd4, 0xd0, 0x35, 0x5e, 0x29, 0x43, - 0xd0, 0xc0, 0xf7, 0x1c, 0x22, 0xa9, 0xab, 0x95, 0x61, 0xed, 0xfc, 0x30, 0x8b, 0x8d, 0xe7, 0x28, - 0x13, 0xff, 0xb3, 0x1c, 0x4e, 0x51, 0xd1, 0x53, 0x28, 0x8e, 0x7c, 0x7e, 0x46, 0x7c, 0xad, 0x09, - 0x6b, 0xe7, 0x61, 0x96, 0x93, 0x3d, 0x8d, 0x58, 0x38, 0x30, 0x14, 0xf4, 0x04, 0x8a, 0x51, 0xe0, - 0x12, 0x49, 0xed, 0xa2, 0x26, 0x37, 0xb3, 0xc8, 0x5f, 0x69, 0xc4, 0x2e, 0x67, 0xe7, 0xde, 0x08, - 0x1b, 0x3c, 0x3a, 0x80, 0x32, 0xa3, 0xf2, 0x6b, 0x2e, 0x2e, 0x42, 0xbb, 0xd4, 0x5c, 0xdd, 0xb2, - 0x76, 0x3e, 0xcd, 0x14, 0x63, 0x8c, 0xe9, 0x48, 0x49, 0x9c, 0xf1, 0x84, 0x32, 0x19, 0xbb, 0xe9, - 0xae, 0xd8, 0x79, 0x3c, 0x77, 0x80, 0x7e, 0x01, 0x65, 0xca, 0xdc, 0x80, 0x7b, 0x4c, 0xda, 0xe5, - 0x9b, 0x03, 0xe9, 0x1b, 0x8c, 0x4a, 0x26, 0x9e, 0x33, 0x14, 0x5b, 0x70, 0xdf, 0x3f, 0x23, 0xce, - 0x85, 0x5d, 0x79, 0xc7, 0x6d, 0xcc, 0x19, 0xdd, 0x22, 0x14, 0x26, 0xdc, 0xa5, 0xad, 0x6d, 0xb8, - 0xf3, 0x46, 0xaa, 0x51, 0x1d, 0xca, 0x26, 0xd5, 0xb1, 0x46, 0x0a, 0x78, 0x3e, 0x6f, 0xdd, 0x86, - 0xf5, 0xa5, 0xb4, 0xb6, 0xfe, 0x5e, 0x80, 0x72, 0x72, 0xd6, 0xa8, 0x03, 0x15, 0x87, 0x33, 0x49, - 0x3c, 0x46, 0x85, 0x91, 0x57, 0xe6, 0xc9, 0xec, 0x26, 0x20, 0xc5, 0x7a, 0x96, 0xc3, 0x0b, 0x16, - 0xfa, 0x0d, 0x54, 0x04, 0x0d, 0x79, 0x24, 0x1c, 0x1a, 0x1a, 0x7d, 0x6d, 0x65, 0x2b, 0x24, 0x06, - 0x61, 0xfa, 0x87, 0xc8, 0x13, 0x54, 0x65, 0x39, 0xc4, 0x0b, 0x2a, 0x7a, 0x0a, 0x25, 0x41, 0x43, - 0x49, 0x84, 0xfc, 0x2e, 0x89, 0xe0, 0x18, 0x32, 0xe0, 0xbe, 0xe7, 0xcc, 0x70, 0xc2, 0x40, 0x4f, - 0xa1, 0x12, 0xf8, 0xc4, 0xd1, 0x5e, 0xed, 0x35, 0x4d, 0xff, 0x20, 0x8b, 0x3e, 0x48, 0x40, 0x78, - 0x81, 0x47, 0x9f, 0x03, 0xf8, 0x7c, 0x34, 0x74, 0x85, 0x37, 0xa5, 0xc2, 0x48, 0xac, 0x9e, 0xc5, - 0xee, 0x69, 0x04, 0xae, 0xf8, 0x7c, 0x14, 0x0f, 0xd1, 0xde, 0xff, 0xa4, 0xaf, 0x94, 0xb6, 0x0e, - 0x00, 0xc8, 0x7c, 0xd5, 0xa8, 0xeb, 0xe3, 0x77, 0x72, 0x65, 0x4e, 0x24, 0x45, 0x47, 0x0f, 0xa1, - 0x7a, 0xce, 0x85, 0x43, 0x87, 0xe6, 0xd6, 0x54, 0xb4, 0x26, 0x2c, 0x6d, 0x8b, 0xf5, 0x85, 0xba, - 0x50, 0x1a, 0x51, 0x46, 0x85, 0xe7, 0xd8, 0xa0, 0x3f, 0xf6, 0x38, 0xf3, 0x42, 0xc6, 0x10, 0x1c, - 0x31, 0xe9, 0x4d, 0xa8, 0xf9, 0x52, 0x42, 0xec, 0x56, 0xa0, 0x24, 0xe2, 0x95, 0xd6, 0xef, 0x01, - 0xbd, 0x89, 0x45, 0x08, 0x0a, 0x17, 0x1e, 0x73, 0xb5, 0xb0, 0x2a, 0x58, 0x8f, 0x51, 0x1b, 0x4a, - 0x01, 0x99, 0xf9, 0x9c, 0xb8, 0x46, 0x2c, 0x77, 0xdb, 0x71, 0xbd, 0x6c, 0x27, 0xf5, 0xb2, 0xdd, - 0x61, 0x33, 0x9c, 0x80, 0x5a, 0x07, 0x70, 0x2f, 0x73, 0xcb, 0x68, 0x07, 0xaa, 0x73, 0x11, 0x0e, - 0x3d, 0xf3, 0x91, 0xee, 0xed, 0xeb, 0xab, 0x4d, 0x6b, 0xae, 0xd6, 0xfd, 0x1e, 0xb6, 0xe6, 0xa0, - 0x7d, 0xb7, 0xf5, 0xa7, 0x0a, 0xac, 0x2f, 0x49, 0x19, 0xdd, 0x85, 0x35, 0x6f, 0x42, 0x46, 0xd4, - 0xc4, 0x18, 0x4f, 0x50, 0x1f, 0x8a, 0x3e, 0x39, 0xa3, 0xbe, 0x12, 0xb4, 0x3a, 0xd4, 0x1f, 0xbd, - 0xf5, 0x4e, 0xb4, 0xbf, 0xd4, 0xf8, 0x3e, 0x93, 0x62, 0x86, 0x0d, 0x19, 0xd9, 0x50, 0x72, 0xf8, - 0x64, 0x42, 0x98, 0x7a, 0x3a, 0x57, 0xb7, 0x2a, 0x38, 0x99, 0xaa, 0xcc, 0x10, 0x31, 0x0a, 0xed, - 0x82, 0x36, 0xeb, 0x31, 0xaa, 0xc1, 0x2a, 0x65, 0x53, 0x7b, 0x4d, 0x9b, 0xd4, 0x50, 0x59, 0x5c, - 0x2f, 0x56, 0x64, 0x05, 0xab, 0xa1, 0xe2, 0x45, 0x21, 0x15, 0x76, 0x29, 0xce, 0xa8, 0x1a, 0xa3, - 0x9f, 0x41, 0x71, 0xc2, 0x23, 0x26, 0x43, 0xbb, 0xac, 0x83, 0xdd, 0xc8, 0x0a, 0xf6, 0x50, 0x21, - 0xcc, 0xd3, 0x6e, 0xe0, 0xa8, 0x0f, 0x77, 0x42, 0xc9, 0x83, 0xe1, 0x48, 0x10, 0x87, 0x0e, 0x03, - 0x2a, 0x3c, 0xee, 0x9a, 0xa7, 0x69, 0xe3, 0x8d, 0x43, 0xe9, 0x99, 0x26, 0x07, 0xdf, 0x56, 0x9c, - 0x3d, 0x45, 0x19, 0x68, 0x06, 0x1a, 0x40, 0x35, 0x88, 0x7c, 0x7f, 0xc8, 0x83, 0xb8, 0x4a, 0xc5, - 0x7a, 0x7a, 0x87, 0x94, 0x0d, 0x22, 0xdf, 0x3f, 0x8e, 0x49, 0xd8, 0x0a, 0x16, 0x13, 0x74, 0x1f, - 0x8a, 0x23, 0xc1, 0xa3, 0x20, 0xb4, 0x2d, 0x9d, 0x0c, 0x33, 0x43, 0x5f, 0x40, 0x29, 0xa4, 0x8e, - 0xa0, 0x32, 0xb4, 0xab, 0x7a, 0xab, 0x1f, 0x66, 0x7d, 0xe4, 0x44, 0x43, 0x30, 0x3d, 0xa7, 0x82, - 0x32, 0x87, 0xe2, 0x84, 0x83, 0x36, 0x60, 0x55, 0xca, 0x99, 0xbd, 0xde, 0xcc, 0x6f, 0x95, 0xbb, - 0xa5, 0xeb, 0xab, 0xcd, 0xd5, 0xd3, 0xd3, 0x17, 0x58, 0xd9, 0xd4, 0x0b, 0x3a, 0xe6, 0xa1, 0x64, - 0x64, 0x42, 0xed, 0x5b, 0x3a, 0xb7, 0xf3, 0x39, 0x7a, 0x01, 0xe0, 0xb2, 0x70, 0xe8, 0xe8, 0x2b, - 0x6b, 0xdf, 0xd6, 0xbb, 0xfb, 0xf4, 0xed, 0xbb, 0xeb, 0x1d, 0x9d, 0x98, 0x2a, 0xb2, 0x7e, 0x7d, - 0xb5, 0x59, 0x99, 0x4f, 0x71, 0xc5, 0x65, 0x61, 0x3c, 0x44, 0x5d, 0xb0, 0xc6, 0x94, 0xf8, 0x72, - 0xec, 0x8c, 0xa9, 0x73, 0x61, 0xd7, 0x6e, 0x2e, 0x0b, 0xcf, 0x34, 0xcc, 0x78, 0x48, 0x93, 0x94, - 0x82, 0x55, 0xa8, 0xa1, 0x7d, 0x47, 0xe7, 0x2a, 0x9e, 0xa0, 0x0f, 0x00, 0x78, 0x40, 0xd9, 0x30, - 0x94, 0xae, 0xc7, 0x6c, 0xa4, 0xb6, 0x8c, 0x2b, 0xca, 0x72, 0xa2, 0x0c, 0xe8, 0x81, 0x7a, 0xb4, - 0x89, 0x3b, 0xe4, 0xcc, 0x9f, 0xd9, 0xdf, 0xd3, 0xab, 0x65, 0x65, 0x38, 0x66, 0xfe, 0x0c, 0x6d, - 0x82, 0xa5, 0x75, 0x11, 0x7a, 0x23, 0x46, 0x7c, 0xfb, 0xae, 0xce, 0x07, 0x28, 0xd3, 0x89, 0xb6, - 0xa8, 0x73, 0x88, 0xb3, 0x11, 0xda, 0xf7, 0x6e, 0x3e, 0x07, 0x13, 0xec, 0xe2, 0x1c, 0x0c, 0x07, - 0xfd, 0x12, 0x20, 0x10, 0xde, 0xd4, 0xf3, 0xe9, 0x88, 0x86, 0xf6, 0x7d, 0xbd, 0xe9, 0x46, 0xe6, - 0x6b, 0x3d, 0x47, 0xe1, 0x14, 0xa3, 0xfe, 0x39, 0x58, 0xa9, 0xdb, 0xa6, 0x6e, 0xc9, 0x05, 0x9d, - 0x99, 0x0b, 0xac, 0x86, 0x2a, 0x25, 0x53, 0xe2, 0x47, 0x71, 0x27, 0x5c, 0xc1, 0xf1, 0xe4, 0xe7, - 0x2b, 0x4f, 0xf2, 0xf5, 0x1d, 0xb0, 0x52, 0xaa, 0x43, 0x1f, 0xc2, 0xba, 0xa0, 0x23, 0x2f, 0x94, - 0x62, 0x36, 0x24, 0x91, 0x1c, 0xdb, 0xbf, 0xd6, 0x84, 0x6a, 0x62, 0xec, 0x44, 0x72, 0x5c, 0x1f, - 0xc2, 0xe2, 0xf0, 0x50, 0x13, 0x2c, 0x25, 0x8a, 0x90, 0x8a, 0x29, 0x15, 0xaa, 0xda, 0xaa, 0x9c, - 0xa7, 0x4d, 0x4a, 0xbc, 0x21, 0x25, 0xc2, 0x19, 0xeb, 0xb7, 0xa3, 0x82, 0xcd, 0x4c, 0x3d, 0x06, - 0xc9, 0x0d, 0x31, 0x8f, 0x81, 0x99, 0xb6, 0xfe, 0x95, 0x87, 0x6a, 0xba, 0x69, 0x40, 0xbb, 0x71, - 0xb1, 0xd7, 0x5b, 0xba, 0xb5, 0xb3, 0xfd, 0xb6, 0x26, 0x43, 0x97, 0x56, 0x3f, 0x52, 0xce, 0x0e, - 0x55, 0x7f, 0xaf, 0xc9, 0xe8, 0xa7, 0xb0, 0x16, 0x70, 0x21, 0x93, 0x27, 0x2c, 0x3b, 0xc1, 0x5c, - 0x24, 0xa5, 0x28, 0x06, 0xb7, 0xc6, 0x70, 0x6b, 0xd9, 0x1b, 0x7a, 0x04, 0xab, 0xcf, 0xf7, 0x07, - 0xb5, 0x5c, 0xfd, 0xc1, 0xcb, 0xcb, 0xe6, 0xf7, 0x97, 0x17, 0x9f, 0x7b, 0x42, 0x46, 0xc4, 0xdf, - 0x1f, 0xa0, 0x4f, 0x60, 0xad, 0x77, 0x74, 0x82, 0x71, 0x2d, 0x5f, 0xdf, 0x7c, 0x79, 0xd9, 0x7c, - 0xb0, 0x8c, 0x53, 0x4b, 0x3c, 0x62, 0x2e, 0xe6, 0x67, 0xf3, 0x5e, 0xf7, 0xdf, 0x2b, 0x60, 0x99, - 0x97, 0xfd, 0x7d, 0xff, 0x1c, 0x5a, 0x8f, 0x4b, 0x79, 0x72, 0x65, 0x57, 0xde, 0x5a, 0xd1, 0xab, - 0x31, 0xc1, 0x9c, 0xf1, 0x43, 0xa8, 0x7a, 0xc1, 0xf4, 0xb3, 0x21, 0x65, 0xe4, 0xcc, 0x37, 0x6d, - 0x6f, 0x19, 0x5b, 0xca, 0xd6, 0x8f, 0x4d, 0xea, 0xbd, 0xf0, 0x98, 0xa4, 0x82, 0x99, 0x86, 0xb6, - 0x8c, 0xe7, 0x73, 0xf4, 0x05, 0x14, 0xbc, 0x80, 0x4c, 0x4c, 0x1b, 0x92, 0xb9, 0x83, 0xfd, 0x41, - 0xe7, 0xd0, 0x68, 0xb0, 0x5b, 0xbe, 0xbe, 0xda, 0x2c, 0x28, 0x03, 0xd6, 0x34, 0xd4, 0x48, 0x3a, - 0x01, 0xf5, 0x25, 0xfd, 0xf6, 0x97, 0x71, 0xca, 0xa2, 0x74, 0xe4, 0xb1, 0x91, 0xa0, 0x61, 0xa8, - 0xab, 0x40, 0x19, 0x27, 0x53, 0x54, 0x87, 0x92, 0xe9, 0x27, 0x74, 0x03, 0x51, 0x51, 0xb5, 0xda, - 0x18, 0xba, 0xeb, 0x60, 0xc5, 0xd9, 0x18, 0x9e, 0x0b, 0x3e, 0x69, 0xfd, 0xa7, 0x00, 0xd6, 0xae, - 0x1f, 0x85, 0xd2, 0x94, 0xc1, 0xf7, 0x96, 0xfc, 0x17, 0x70, 0x87, 0xe8, 0x9f, 0x57, 0x84, 0xa9, - 0x9a, 0xa2, 0xdb, 0x34, 0x73, 0x00, 0x8f, 0x32, 0xdd, 0xcd, 0xc1, 0x71, 0x4b, 0xd7, 0x2d, 0x2a, - 0x9f, 0x76, 0x1e, 0xd7, 0xc8, 0x6b, 0x2b, 0xe8, 0x04, 0xd6, 0xb9, 0x70, 0xc6, 0x34, 0x94, 0x71, - 0x25, 0x32, 0x3f, 0x47, 0x32, 0x7f, 0xa8, 0x1e, 0xa7, 0x81, 0xe6, 0x19, 0x8e, 0xa3, 0x5d, 0xf6, - 0x81, 0x9e, 0x40, 0x41, 0x90, 0xf3, 0xa4, 0xe5, 0xcc, 0xbc, 0x24, 0x98, 0x9c, 0xcb, 0x25, 0x17, - 0x9a, 0x81, 0x7e, 0x0b, 0xe0, 0x7a, 0x61, 0x40, 0xa4, 0x33, 0xa6, 0xc2, 0x1c, 0x76, 0xe6, 0x16, - 0x7b, 0x73, 0xd4, 0x92, 0x97, 0x14, 0x1b, 0x1d, 0x40, 0xc5, 0x21, 0x89, 0x5c, 0x8b, 0x37, 0xff, - 0x46, 0xdb, 0xed, 0x18, 0x17, 0x35, 0xe5, 0xe2, 0xfa, 0x6a, 0xb3, 0x9c, 0x58, 0x70, 0xd9, 0x21, - 0x46, 0xbe, 0x07, 0xb0, 0xae, 0x7e, 0xbb, 0x0d, 0x5d, 0x7a, 0x4e, 0x22, 0x5f, 0xc6, 0x32, 0xb9, - 0xa1, 0xac, 0xa8, 0x1f, 0x02, 0x3d, 0x83, 0x33, 0x71, 0x55, 0x65, 0xca, 0x86, 0x7e, 0x07, 0x77, - 0x28, 0x73, 0xc4, 0x4c, 0x8b, 0x35, 0x89, 0xb0, 0x7c, 0xf3, 0x66, 0xfb, 0x73, 0xf0, 0xd2, 0x66, - 0x6b, 0xf4, 0x35, 0x7b, 0xeb, 0xaf, 0x79, 0x80, 0xb8, 0x52, 0xbf, 0x5f, 0x01, 0x22, 0x28, 0xb8, - 0x44, 0x12, 0xad, 0xb9, 0x2a, 0xd6, 0x63, 0x55, 0x70, 0x24, 0x9d, 0x04, 0x3e, 0x91, 0x1e, 0x1b, - 0x99, 0xff, 0x37, 0x32, 0x8f, 0xfa, 0x74, 0x8e, 0xc2, 0x29, 0x86, 0x8e, 0x35, 0x0e, 0xfb, 0xff, - 0x3e, 0xd6, 0x4f, 0x3e, 0x02, 0x58, 0xac, 0xa0, 0x32, 0x14, 0x8e, 0x8e, 0x8f, 0xfa, 0xb5, 0x1c, - 0xba, 0x0d, 0xd6, 0xde, 0xf1, 0xf0, 0xb4, 0x7f, 0x38, 0xf8, 0xb2, 0x73, 0xda, 0xaf, 0xe5, 0xbb, - 0xf6, 0xab, 0x6f, 0x1b, 0xb9, 0x7f, 0x7c, 0xdb, 0xc8, 0xfd, 0xf1, 0xba, 0x91, 0x7f, 0x75, 0xdd, - 0xc8, 0xff, 0xed, 0xba, 0x91, 0xff, 0xe7, 0x75, 0x23, 0x7f, 0x56, 0xd4, 0x4d, 0xdf, 0x4f, 0xfe, - 0x1b, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x7b, 0xf5, 0x7d, 0x2d, 0x13, 0x00, 0x00, + // 1867 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcf, 0x73, 0x1b, 0x49, + 0x15, 0xb6, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xd2, 0x24, 0x61, 0xa2, 0xb0, 0xb2, 0xa2, 0x0d, + 0xc1, 0xcb, 0x16, 0x72, 0x61, 0xa8, 0x25, 0xbb, 0x61, 0x01, 0xc9, 0x12, 0x8e, 0x31, 0x76, 0x54, + 0x6d, 0x6f, 0x20, 0x27, 0x55, 0x7b, 0xa6, 0x3d, 0x9a, 0xf2, 0xa8, 0x7b, 0xe8, 0xe9, 0xd1, 0x96, + 0x6e, 0x1c, 0xb7, 0x72, 0xe5, 0xec, 0xe2, 0x40, 0xf1, 0xbf, 0xe4, 0x48, 0x71, 0xe2, 0xe4, 0x62, + 0xfd, 0x2f, 0x70, 0xe3, 0x02, 0xd5, 0x3d, 0x3d, 0xd2, 0x28, 0x19, 0x27, 0xa9, 0x22, 0x07, 0x6e, + 0xdd, 0xaf, 0xbf, 0xef, 0xcd, 0xeb, 0xd7, 0x5f, 0xf7, 0x7b, 0x03, 0x56, 0x14, 0x52, 0x27, 0xea, + 0x84, 0x82, 0x4b, 0x8e, 0x90, 0xcb, 0x9d, 0x73, 0x2a, 0x3a, 0xd1, 0xd7, 0x44, 0x4c, 0xce, 0x7d, + 0xd9, 0x99, 0xfe, 0xb8, 0x61, 0xc9, 0x59, 0x48, 0x0d, 0xa0, 0x71, 0xdb, 0xe3, 0x1e, 0xd7, 0xc3, + 0x6d, 0x35, 0x32, 0xd6, 0xa6, 0xc7, 0xb9, 0x17, 0xd0, 0x6d, 0x3d, 0x3b, 0x8d, 0xcf, 0xb6, 0xdd, + 0x58, 0x10, 0xe9, 0x73, 0x66, 0xd6, 0xef, 0xbd, 0xbe, 0x4e, 0xd8, 0x2c, 0x59, 0x6a, 0x5f, 0x14, + 0xa1, 0x72, 0xc4, 0x5d, 0x7a, 0x1c, 0x52, 0x07, 0xed, 0x81, 0x45, 0x18, 0xe3, 0x52, 0x73, 0x23, + 0xbb, 0xd0, 0x2a, 0x6c, 0x59, 0x3b, 0x9b, 0x9d, 0x37, 0x83, 0xea, 0x74, 0x17, 0xb0, 0x5e, 0xf1, + 0xd5, 0xe5, 0xe6, 0x0a, 0xce, 0x32, 0xd1, 0x2f, 0xa1, 0xe6, 0xd2, 0xc8, 0x17, 0xd4, 0x1d, 0x09, + 0x1e, 0x50, 0x7b, 0xb5, 0x55, 0xd8, 0xba, 0xb1, 0xf3, 0xbd, 0x3c, 0x4f, 0xea, 0xe3, 0x98, 0x07, + 0x14, 0x5b, 0x86, 0xa1, 0x26, 0x68, 0x0f, 0x60, 0x42, 0x27, 0xa7, 0x54, 0x44, 0x63, 0x3f, 0xb4, + 0xd7, 0x34, 0xfd, 0x07, 0xd7, 0xd1, 0x55, 0xec, 0x9d, 0xc3, 0x39, 0x1c, 0x67, 0xa8, 0xe8, 0x10, + 0x6a, 0x64, 0x4a, 0xfc, 0x80, 0x9c, 0xfa, 0x81, 0x2f, 0x67, 0x76, 0x51, 0xbb, 0xfa, 0xe4, 0xad, + 0xae, 0xba, 0x19, 0x02, 0x5e, 0xa2, 0xb7, 0x5d, 0x80, 0xc5, 0x87, 0xd0, 0x23, 0x28, 0x0f, 0x07, + 0x47, 0xfd, 0xfd, 0xa3, 0xbd, 0xfa, 0x4a, 0xe3, 0xde, 0xcb, 0x8b, 0xd6, 0x1d, 0xe5, 0x63, 0x01, + 0x18, 0x52, 0xe6, 0xfa, 0xcc, 0x43, 0x5b, 0x50, 0xe9, 0xee, 0xee, 0x0e, 0x86, 0x27, 0x83, 0x7e, + 0xbd, 0xd0, 0x68, 0xbc, 0xbc, 0x68, 0xdd, 0x5d, 0x06, 0x76, 0x1d, 0x87, 0x86, 0x92, 0xba, 0x8d, + 0xe2, 0x37, 0x7f, 0x69, 0xae, 0xb4, 0xbf, 0x29, 0x40, 0x2d, 0x1b, 0x04, 0x7a, 0x04, 0xa5, 0xee, + 0xee, 0xc9, 0xfe, 0xf3, 0x41, 0x7d, 0x65, 0x41, 0xcf, 0x22, 0xba, 0x8e, 0xf4, 0xa7, 0x14, 0x3d, + 0x84, 0xf5, 0x61, 0xf7, 0xab, 0xe3, 0x41, 0xbd, 0xb0, 0x08, 0x27, 0x0b, 0x1b, 0x92, 0x38, 0xd2, + 0xa8, 0x3e, 0xee, 0xee, 0x1f, 0xd5, 0x57, 0xf3, 0x51, 0x7d, 0x41, 0x7c, 0x66, 0x42, 0xf9, 0x73, + 0x11, 0xac, 0x63, 0x2a, 0xa6, 0xbe, 0xf3, 0x81, 0x25, 0xf2, 0x19, 0x14, 0x25, 0x89, 0xce, 0xb5, + 0x34, 0xac, 0x7c, 0x69, 0x9c, 0x90, 0xe8, 0x5c, 0x7d, 0xd4, 0xd0, 0x35, 0x5e, 0x29, 0x43, 0xd0, + 0x30, 0xf0, 0x1d, 0x22, 0xa9, 0xab, 0x95, 0x61, 0xed, 0x7c, 0x3f, 0x8f, 0x8d, 0xe7, 0x28, 0x13, + 0xff, 0xd3, 0x15, 0x9c, 0xa1, 0xa2, 0x27, 0x50, 0xf2, 0x02, 0x7e, 0x4a, 0x02, 0xad, 0x09, 0x6b, + 0xe7, 0x41, 0x9e, 0x93, 0x3d, 0x8d, 0x58, 0x38, 0x30, 0x14, 0xf4, 0x18, 0x4a, 0x71, 0xe8, 0x12, + 0x49, 0xed, 0x92, 0x26, 0xb7, 0xf2, 0xc8, 0x5f, 0x69, 0xc4, 0x2e, 0x67, 0x67, 0xbe, 0x87, 0x0d, + 0x1e, 0x1d, 0x40, 0x85, 0x51, 0xf9, 0x35, 0x17, 0xe7, 0x91, 0x5d, 0x6e, 0xad, 0x6d, 0x59, 0x3b, + 0x9f, 0xe6, 0x8a, 0x31, 0xc1, 0x74, 0xa5, 0x24, 0xce, 0x78, 0x42, 0x99, 0x4c, 0xdc, 0xf4, 0x56, + 0xed, 0x02, 0x9e, 0x3b, 0x40, 0x3f, 0x87, 0x0a, 0x65, 0x6e, 0xc8, 0x7d, 0x26, 0xed, 0xca, 0xf5, + 0x81, 0x0c, 0x0c, 0x46, 0x25, 0x13, 0xcf, 0x19, 0x8a, 0x2d, 0x78, 0x10, 0x9c, 0x12, 0xe7, 0xdc, + 0xae, 0xbe, 0xe7, 0x36, 0xe6, 0x8c, 0x5e, 0x09, 0x8a, 0x13, 0xee, 0xd2, 0xf6, 0x36, 0xdc, 0x7a, + 0x23, 0xd5, 0xa8, 0x01, 0x15, 0x93, 0xea, 0x44, 0x23, 0x45, 0x3c, 0x9f, 0xb7, 0x6f, 0xc2, 0xc6, + 0x52, 0x5a, 0xdb, 0x7f, 0x2f, 0x42, 0x25, 0x3d, 0x6b, 0xd4, 0x85, 0xaa, 0xc3, 0x99, 0x24, 0x3e, + 0xa3, 0xc2, 0xc8, 0x2b, 0xf7, 0x64, 0x76, 0x53, 0x90, 0x62, 0x3d, 0x5d, 0xc1, 0x0b, 0x16, 0xfa, + 0x35, 0x54, 0x05, 0x8d, 0x78, 0x2c, 0x1c, 0x1a, 0x19, 0x7d, 0x6d, 0xe5, 0x2b, 0x24, 0x01, 0x61, + 0xfa, 0x87, 0xd8, 0x17, 0x54, 0x65, 0x39, 0xc2, 0x0b, 0x2a, 0x7a, 0x02, 0x65, 0x41, 0x23, 0x49, + 0x84, 0x7c, 0x9b, 0x44, 0x70, 0x02, 0x19, 0xf2, 0xc0, 0x77, 0x66, 0x38, 0x65, 0xa0, 0x27, 0x50, + 0x0d, 0x03, 0xe2, 0x68, 0xaf, 0xf6, 0xba, 0xa6, 0x7f, 0x94, 0x47, 0x1f, 0xa6, 0x20, 0xbc, 0xc0, + 0xa3, 0xcf, 0x01, 0x02, 0xee, 0x8d, 0x5c, 0xe1, 0x4f, 0xa9, 0x30, 0x12, 0x6b, 0xe4, 0xb1, 0xfb, + 0x1a, 0x81, 0xab, 0x01, 0xf7, 0x92, 0x21, 0xda, 0xfb, 0x9f, 0xf4, 0x95, 0xd1, 0xd6, 0x01, 0x00, + 0x99, 0xaf, 0x1a, 0x75, 0x7d, 0xf2, 0x5e, 0xae, 0xcc, 0x89, 0x64, 0xe8, 0xe8, 0x01, 0xd4, 0xce, + 0xb8, 0x70, 0xe8, 0xc8, 0xdc, 0x9a, 0xaa, 0xd6, 0x84, 0xa5, 0x6d, 0x89, 0xbe, 0x50, 0x0f, 0xca, + 0x1e, 0x65, 0x54, 0xf8, 0x8e, 0x0d, 0xfa, 0x63, 0x8f, 0x72, 0x2f, 0x64, 0x02, 0xc1, 0x31, 0x93, + 0xfe, 0x84, 0x9a, 0x2f, 0xa5, 0xc4, 0x5e, 0x15, 0xca, 0x22, 0x59, 0x69, 0xff, 0x1e, 0xd0, 0x9b, + 0x58, 0x84, 0xa0, 0x78, 0xee, 0x33, 0x57, 0x0b, 0xab, 0x8a, 0xf5, 0x18, 0x75, 0xa0, 0x1c, 0x92, + 0x59, 0xc0, 0x89, 0x6b, 0xc4, 0x72, 0xbb, 0x93, 0xd4, 0xcb, 0x4e, 0x5a, 0x2f, 0x3b, 0x5d, 0x36, + 0xc3, 0x29, 0xa8, 0x7d, 0x00, 0x77, 0x72, 0xb7, 0x8c, 0x76, 0xa0, 0x36, 0x17, 0xe1, 0xc8, 0x37, + 0x1f, 0xe9, 0xdd, 0xbc, 0xba, 0xdc, 0xb4, 0xe6, 0x6a, 0xdd, 0xef, 0x63, 0x6b, 0x0e, 0xda, 0x77, + 0xdb, 0x7f, 0xaa, 0xc2, 0xc6, 0x92, 0x94, 0xd1, 0x6d, 0x58, 0xf7, 0x27, 0xc4, 0xa3, 0x26, 0xc6, + 0x64, 0x82, 0x06, 0x50, 0x0a, 0xc8, 0x29, 0x0d, 0x94, 0xa0, 0xd5, 0xa1, 0xfe, 0xe8, 0x9d, 0x77, + 0xa2, 0xf3, 0x5b, 0x8d, 0x1f, 0x30, 0x29, 0x66, 0xd8, 0x90, 0x91, 0x0d, 0x65, 0x87, 0x4f, 0x26, + 0x84, 0xa9, 0xa7, 0x73, 0x6d, 0xab, 0x8a, 0xd3, 0xa9, 0xca, 0x0c, 0x11, 0x5e, 0x64, 0x17, 0xb5, + 0x59, 0x8f, 0x51, 0x1d, 0xd6, 0x28, 0x9b, 0xda, 0xeb, 0xda, 0xa4, 0x86, 0xca, 0xe2, 0xfa, 0x89, + 0x22, 0xab, 0x58, 0x0d, 0x15, 0x2f, 0x8e, 0xa8, 0xb0, 0xcb, 0x49, 0x46, 0xd5, 0x18, 0xfd, 0x0c, + 0x4a, 0x13, 0x1e, 0x33, 0x19, 0xd9, 0x15, 0x1d, 0xec, 0xbd, 0xbc, 0x60, 0x0f, 0x15, 0xc2, 0x3c, + 0xed, 0x06, 0x8e, 0x06, 0x70, 0x2b, 0x92, 0x3c, 0x1c, 0x79, 0x82, 0x38, 0x74, 0x14, 0x52, 0xe1, + 0x73, 0xd7, 0x3c, 0x4d, 0xf7, 0xde, 0x38, 0x94, 0xbe, 0x69, 0x72, 0xf0, 0x4d, 0xc5, 0xd9, 0x53, + 0x94, 0xa1, 0x66, 0xa0, 0x21, 0xd4, 0xc2, 0x38, 0x08, 0x46, 0x3c, 0x4c, 0xaa, 0x54, 0xa2, 0xa7, + 0xf7, 0x48, 0xd9, 0x30, 0x0e, 0x82, 0x67, 0x09, 0x09, 0x5b, 0xe1, 0x62, 0x82, 0xee, 0x42, 0xc9, + 0x13, 0x3c, 0x0e, 0x23, 0xdb, 0xd2, 0xc9, 0x30, 0x33, 0xf4, 0x25, 0x94, 0x23, 0xea, 0x08, 0x2a, + 0x23, 0xbb, 0xa6, 0xb7, 0xfa, 0x71, 0xde, 0x47, 0x8e, 0x35, 0x04, 0xd3, 0x33, 0x2a, 0x28, 0x73, + 0x28, 0x4e, 0x39, 0xe8, 0x1e, 0xac, 0x49, 0x39, 0xb3, 0x37, 0x5a, 0x85, 0xad, 0x4a, 0xaf, 0x7c, + 0x75, 0xb9, 0xb9, 0x76, 0x72, 0xf2, 0x02, 0x2b, 0x9b, 0x7a, 0x41, 0xc7, 0x3c, 0x92, 0x8c, 0x4c, + 0xa8, 0x7d, 0x43, 0xe7, 0x76, 0x3e, 0x47, 0x2f, 0x00, 0x5c, 0x16, 0x8d, 0x1c, 0x7d, 0x65, 0xed, + 0x9b, 0x7a, 0x77, 0x9f, 0xbe, 0x7b, 0x77, 0xfd, 0xa3, 0x63, 0x53, 0x45, 0x36, 0xae, 0x2e, 0x37, + 0xab, 0xf3, 0x29, 0xae, 0xba, 0x2c, 0x4a, 0x86, 0xa8, 0x07, 0xd6, 0x98, 0x92, 0x40, 0x8e, 0x9d, + 0x31, 0x75, 0xce, 0xed, 0xfa, 0xf5, 0x65, 0xe1, 0xa9, 0x86, 0x19, 0x0f, 0x59, 0x92, 0x52, 0xb0, + 0x0a, 0x35, 0xb2, 0x6f, 0xe9, 0x5c, 0x25, 0x13, 0xf4, 0x11, 0x00, 0x0f, 0x29, 0x1b, 0x45, 0xd2, + 0xf5, 0x99, 0x8d, 0xd4, 0x96, 0x71, 0x55, 0x59, 0x8e, 0x95, 0x01, 0xdd, 0x57, 0x8f, 0x36, 0x71, + 0x47, 0x9c, 0x05, 0x33, 0xfb, 0x3b, 0x7a, 0xb5, 0xa2, 0x0c, 0xcf, 0x58, 0x30, 0x43, 0x9b, 0x60, + 0x69, 0x5d, 0x44, 0xbe, 0xc7, 0x48, 0x60, 0xdf, 0xd6, 0xf9, 0x00, 0x65, 0x3a, 0xd6, 0x16, 0x75, + 0x0e, 0x49, 0x36, 0x22, 0xfb, 0xce, 0xf5, 0xe7, 0x60, 0x82, 0x5d, 0x9c, 0x83, 0xe1, 0xa0, 0x5f, + 0x00, 0x84, 0xc2, 0x9f, 0xfa, 0x01, 0xf5, 0x68, 0x64, 0xdf, 0xd5, 0x9b, 0x6e, 0xe6, 0xbe, 0xd6, + 0x73, 0x14, 0xce, 0x30, 0x1a, 0x9f, 0x83, 0x95, 0xb9, 0x6d, 0xea, 0x96, 0x9c, 0xd3, 0x99, 0xb9, + 0xc0, 0x6a, 0xa8, 0x52, 0x32, 0x25, 0x41, 0x9c, 0x74, 0xc2, 0x55, 0x9c, 0x4c, 0xbe, 0x58, 0x7d, + 0x5c, 0x68, 0xec, 0x80, 0x95, 0x51, 0x1d, 0xfa, 0x18, 0x36, 0x04, 0xf5, 0xfc, 0x48, 0x8a, 0xd9, + 0x88, 0xc4, 0x72, 0x6c, 0xff, 0x4a, 0x13, 0x6a, 0xa9, 0xb1, 0x1b, 0xcb, 0x71, 0x63, 0x04, 0x8b, + 0xc3, 0x43, 0x2d, 0xb0, 0x94, 0x28, 0x22, 0x2a, 0xa6, 0x54, 0xa8, 0x6a, 0xab, 0x72, 0x9e, 0x35, + 0x29, 0xf1, 0x46, 0x94, 0x08, 0x67, 0xac, 0xdf, 0x8e, 0x2a, 0x36, 0x33, 0xf5, 0x18, 0xa4, 0x37, + 0xc4, 0x3c, 0x06, 0x66, 0xda, 0xfe, 0x57, 0x01, 0x6a, 0xd9, 0xa6, 0x01, 0xed, 0x26, 0xc5, 0x5e, + 0x6f, 0xe9, 0xc6, 0xce, 0xf6, 0xbb, 0x9a, 0x0c, 0x5d, 0x5a, 0x83, 0x58, 0x39, 0x3b, 0x54, 0xfd, + 0xbd, 0x26, 0xa3, 0x9f, 0xc2, 0x7a, 0xc8, 0x85, 0x4c, 0x9f, 0xb0, 0xfc, 0x04, 0x73, 0x91, 0x96, + 0xa2, 0x04, 0xdc, 0x1e, 0xc3, 0x8d, 0x65, 0x6f, 0xe8, 0x21, 0xac, 0x3d, 0xdf, 0x1f, 0xd6, 0x57, + 0x1a, 0xf7, 0x5f, 0x5e, 0xb4, 0xbe, 0xbb, 0xbc, 0xf8, 0xdc, 0x17, 0x32, 0x26, 0xc1, 0xfe, 0x10, + 0xfd, 0x10, 0xd6, 0xfb, 0x47, 0xc7, 0x18, 0xd7, 0x0b, 0x8d, 0xcd, 0x97, 0x17, 0xad, 0xfb, 0xcb, + 0x38, 0xb5, 0xc4, 0x63, 0xe6, 0x62, 0x7e, 0x3a, 0xef, 0x75, 0xff, 0xbd, 0x0a, 0x96, 0x79, 0xd9, + 0x3f, 0xf4, 0xef, 0xd0, 0x46, 0x52, 0xca, 0xd3, 0x2b, 0xbb, 0xfa, 0xce, 0x8a, 0x5e, 0x4b, 0x08, + 0xe6, 0x8c, 0x1f, 0x40, 0xcd, 0x0f, 0xa7, 0x9f, 0x8d, 0x28, 0x23, 0xa7, 0x81, 0x69, 0x7b, 0x2b, + 0xd8, 0x52, 0xb6, 0x41, 0x62, 0x52, 0xef, 0x85, 0xcf, 0x24, 0x15, 0xcc, 0x34, 0xb4, 0x15, 0x3c, + 0x9f, 0xa3, 0x2f, 0xa1, 0xe8, 0x87, 0x64, 0x62, 0xda, 0x90, 0xdc, 0x1d, 0xec, 0x0f, 0xbb, 0x87, + 0x46, 0x83, 0xbd, 0xca, 0xd5, 0xe5, 0x66, 0x51, 0x19, 0xb0, 0xa6, 0xa1, 0x66, 0xda, 0x09, 0xa8, + 0x2f, 0xe9, 0xb7, 0xbf, 0x82, 0x33, 0x16, 0xa5, 0x23, 0x9f, 0x79, 0x82, 0x46, 0x91, 0xae, 0x02, + 0x15, 0x9c, 0x4e, 0x51, 0x03, 0xca, 0xa6, 0x9f, 0xd0, 0x0d, 0x44, 0x55, 0xd5, 0x6a, 0x63, 0xe8, + 0x6d, 0x80, 0x95, 0x64, 0x63, 0x74, 0x26, 0xf8, 0xa4, 0xfd, 0x9f, 0x22, 0x58, 0xbb, 0x41, 0x1c, + 0x49, 0x53, 0x06, 0x3f, 0x58, 0xf2, 0x5f, 0xc0, 0x2d, 0xa2, 0x7f, 0xaf, 0x08, 0x53, 0x35, 0x45, + 0xb7, 0x69, 0xe6, 0x00, 0x1e, 0xe6, 0xba, 0x9b, 0x83, 0x93, 0x96, 0xae, 0x57, 0x52, 0x3e, 0xed, + 0x02, 0xae, 0x93, 0xd7, 0x56, 0xd0, 0x31, 0x6c, 0x70, 0xe1, 0x8c, 0x69, 0x24, 0x93, 0x4a, 0x64, + 0x7e, 0x47, 0x72, 0x7f, 0x54, 0x9f, 0x65, 0x81, 0xe6, 0x19, 0x4e, 0xa2, 0x5d, 0xf6, 0x81, 0x1e, + 0x43, 0x51, 0x90, 0xb3, 0xb4, 0xe5, 0xcc, 0xbd, 0x24, 0x98, 0x9c, 0xc9, 0x25, 0x17, 0x9a, 0x81, + 0x7e, 0x03, 0xe0, 0xfa, 0x51, 0x48, 0xa4, 0x33, 0xa6, 0xc2, 0x1c, 0x76, 0xee, 0x16, 0xfb, 0x73, + 0xd4, 0x92, 0x97, 0x0c, 0x1b, 0x1d, 0x40, 0xd5, 0x21, 0xa9, 0x5c, 0x4b, 0xd7, 0xff, 0xa3, 0xed, + 0x76, 0x8d, 0x8b, 0xba, 0x72, 0x71, 0x75, 0xb9, 0x59, 0x49, 0x2d, 0xb8, 0xe2, 0x10, 0x23, 0xdf, + 0x03, 0xd8, 0x50, 0xff, 0x6e, 0x23, 0x97, 0x9e, 0x91, 0x38, 0x90, 0x89, 0x4c, 0xae, 0x29, 0x2b, + 0xea, 0x47, 0xa0, 0x6f, 0x70, 0x26, 0xae, 0x9a, 0xcc, 0xd8, 0xd0, 0xef, 0xe0, 0x16, 0x65, 0x8e, + 0x98, 0x69, 0xb1, 0xa6, 0x11, 0x56, 0xae, 0xdf, 0xec, 0x60, 0x0e, 0x5e, 0xda, 0x6c, 0x9d, 0xbe, + 0x66, 0x6f, 0xff, 0xb5, 0x00, 0x90, 0x54, 0xea, 0x0f, 0x2b, 0x40, 0x04, 0x45, 0x97, 0x48, 0xa2, + 0x35, 0x57, 0xc3, 0x7a, 0x8c, 0xbe, 0x00, 0x90, 0x74, 0x12, 0x06, 0x44, 0xfa, 0xcc, 0x33, 0xb2, + 0x79, 0xdb, 0x73, 0x90, 0x41, 0xeb, 0x38, 0x93, 0x90, 0xff, 0xaf, 0xe3, 0xec, 0xd9, 0xaf, 0xbe, + 0x6d, 0xae, 0xfc, 0xe3, 0xdb, 0xe6, 0xca, 0x1f, 0xaf, 0x9a, 0x85, 0x57, 0x57, 0xcd, 0xc2, 0xdf, + 0xae, 0x9a, 0x85, 0x7f, 0x5e, 0x35, 0x0b, 0xa7, 0x25, 0xdd, 0xc3, 0xfd, 0xe4, 0xbf, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x06, 0x93, 0x6e, 0xba, 0xfc, 0x12, 0x00, 0x00, } diff --git a/api/specs.proto b/api/specs.proto index 31d3873c47..13b52d4105 100644 --- a/api/specs.proto +++ b/api/specs.proto @@ -380,13 +380,6 @@ message ClusterSpec { EncryptionConfig encryption_config = 8 [(gogoproto.nullable) = false]; } -// Templating controls whether the payload of a config or secret is evaluated -// as a template. -enum Templating { - NONE = 0; - GO_TEMPLATE = 1; -} - // SecretSpec specifies a user-provided secret. message SecretSpec { Annotations annotations = 1 [(gogoproto.nullable) = false]; @@ -395,8 +388,11 @@ message SecretSpec { bytes data = 2; // Templating controls whether and how to evaluate the secret payload as - // a template. - Templating templating = 3; + // a template. If it is not set, no templating is used. + // + // The currently recognized values are: + // - golang: Go templating + Driver templating = 3; } // ConfigSpec specifies user-provided configuration files. @@ -408,7 +404,10 @@ message ConfigSpec { // ConfigSpec? Define this to be a tar? etc... bytes data = 2; - // Templating controls whether and how to evaluate the config payload as - // a template. - Templating templating = 3; + // Templating controls whether and how to evaluate the secret payload as + // a template. If it is not set, no templating is used. + // + // The currently recognized values are: + // - golang: Go templating + Driver templating = 3; } diff --git a/template/expand.go b/template/expand.go index fc8e18f074..e45c36252d 100644 --- a/template/expand.go +++ b/template/expand.go @@ -129,35 +129,33 @@ func expandPayload(ctx PayloadContext, payload []byte) ([]byte, error) { // ExpandSecretSpec expands the template inside the secret payload, if any. // Templating is evaluated on the agent-side. func ExpandSecretSpec(s *api.Secret, t *api.Task, dependencies exec.DependencyGetter) (*api.SecretSpec, error) { - switch s.Spec.Templating { - case api.Templating_NONE: + if s.Spec.Templating == nil { return &s.Spec, nil - case api.Templating_GO_TEMPLATE: + } + if s.Spec.Templating.Name == "golang" { ctx := NewPayloadContextFromTask(t, dependencies) secretSpec := s.Spec.Copy() var err error secretSpec.Data, err = expandPayload(ctx, secretSpec.Data) return secretSpec, err - default: - return &s.Spec, errors.New("unrecognized template type") } + return &s.Spec, errors.New("unrecognized template type") } // ExpandConfigSpec expands the template inside the config payload, if any. // Templating is evaluated on the agent-side. func ExpandConfigSpec(c *api.Config, t *api.Task, dependencies exec.DependencyGetter) (*api.ConfigSpec, error) { - switch c.Spec.Templating { - case api.Templating_NONE: + if c.Spec.Templating == nil { return &c.Spec, nil - case api.Templating_GO_TEMPLATE: + } + if c.Spec.Templating.Name == "golang" { ctx := NewPayloadContextFromTask(t, dependencies) configSpec := c.Spec.Copy() var err error configSpec.Data, err = expandPayload(ctx, configSpec.Data) return configSpec, err - default: - return &c.Spec, errors.New("unrecognized template type") } + return &c.Spec, errors.New("unrecognized template type") } diff --git a/template/getter_test.go b/template/getter_test.go index deadbf3f9d..88ea0570ec 100644 --- a/template/getter_test.go +++ b/template/getter_test.go @@ -44,7 +44,7 @@ func TestTemplatedSecret(t *testing.T) { "TASK_ID={{.Task.ID}}\n" + "TASK_NAME={{.Task.Name}}\n" + "NODE_ID={{.Node.ID}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "SERVICE_ID=serviceID\n" + "SERVICE_NAME=serviceName\n" + @@ -70,7 +70,7 @@ func TestTemplatedSecret(t *testing.T) { desc: "Test expansion of secret, by target", secretSpec: api.SecretSpec{ Data: []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "SECRET_VAL=mysecret\n", task: modifyTask(func(t *api.Task) { @@ -104,7 +104,7 @@ func TestTemplatedSecret(t *testing.T) { desc: "Test expansion of config, by target", secretSpec: api.SecretSpec{ Data: []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "CONFIG_VAL=myconfig\n", task: modifyTask(func(t *api.Task) { @@ -140,7 +140,7 @@ func TestTemplatedSecret(t *testing.T) { desc: "Test expansion of secret not available to task", secretSpec: api.SecretSpec{ Data: []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling secret: secret target unknowntarget not found`, task: modifyTask(func(t *api.Task) { @@ -162,7 +162,7 @@ func TestTemplatedSecret(t *testing.T) { desc: "Test expansion of config not available to task", secretSpec: api.SecretSpec{ Data: []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at : error calling config: config target unknowntarget not found`, task: modifyTask(func(t *api.Task) { @@ -184,7 +184,7 @@ func TestTemplatedSecret(t *testing.T) { desc: "Test that expansion of the same secret avoids recursion", secretSpec: api.SecretSpec{ Data: []byte("SECRET_VAL={{secret \"templatedsecrettarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "SECRET_VAL=SECRET_VAL={{secret \"templatedsecrettarget\"}}\n\n", task: modifyTask(func(t *api.Task) { @@ -215,7 +215,7 @@ func TestTemplatedSecret(t *testing.T) { secretSpec: api.SecretSpec{ Data: []byte("ENV VALUE={{env \"foo\"}}\n" + "DOES NOT EXIST={{env \"badname\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "ENV VALUE=bar\n" + "DOES NOT EXIST=\n", @@ -292,7 +292,7 @@ func TestTemplatedConfig(t *testing.T) { "TASK_ID={{.Task.ID}}\n" + "TASK_NAME={{.Task.Name}}\n" + "NODE_ID={{.Node.ID}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "SERVICE_ID=serviceID\n" + "SERVICE_NAME=serviceName\n" + @@ -318,7 +318,7 @@ func TestTemplatedConfig(t *testing.T) { desc: "Test expansion of secret, by target", configSpec: api.ConfigSpec{ Data: []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "SECRET_VAL=mysecret\n", task: modifyTask(func(t *api.Task) { @@ -354,7 +354,7 @@ func TestTemplatedConfig(t *testing.T) { desc: "Test expansion of config, by target", configSpec: api.ConfigSpec{ Data: []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "CONFIG_VAL=myconfig\n", task: modifyTask(func(t *api.Task) { @@ -388,7 +388,7 @@ func TestTemplatedConfig(t *testing.T) { desc: "Test expansion of secret not available to task", configSpec: api.ConfigSpec{ Data: []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling secret: secret target unknowntarget not found`, task: modifyTask(func(t *api.Task) { @@ -410,7 +410,7 @@ func TestTemplatedConfig(t *testing.T) { desc: "Test expansion of config not available to task", configSpec: api.ConfigSpec{ Data: []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at : error calling config: config target unknowntarget not found`, task: modifyTask(func(t *api.Task) { @@ -432,7 +432,7 @@ func TestTemplatedConfig(t *testing.T) { desc: "Test that expansion of the same config avoids recursion", configSpec: api.ConfigSpec{ Data: []byte("CONFIG_VAL={{config \"templatedconfigtarget\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "CONFIG_VAL=CONFIG_VAL={{config \"templatedconfigtarget\"}}\n\n", task: modifyTask(func(t *api.Task) { @@ -463,7 +463,7 @@ func TestTemplatedConfig(t *testing.T) { configSpec: api.ConfigSpec{ Data: []byte("ENV VALUE={{env \"foo\"}}\n" + "DOES NOT EXIST={{env \"badname\"}}\n"), - Templating: api.Templating_GO_TEMPLATE, + Templating: &api.Driver{Name: "golang"}, }, expected: "ENV VALUE=bar\n" + "DOES NOT EXIST=\n", From ef30818d949c7df4881adbc6729d213a227dedd8 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Wed, 14 Jun 2017 17:57:34 -0700 Subject: [PATCH 06/12] Fix TestRenewTLSConfigUpdatesRootOnUnknownAuthError instability: there was a timing issue where during the renew, the CA server made a request to the external CA right before/right at the time of updating the root CA. By the time the external CA responded, the root had already been rotated and a different intermediate had been added to the external CA, and the URL that had been hit had already been removed from the external CA. In normal operation, RenewTLSConfig would just retry later, but the test does not retry, and hence occasionally fails. Update the test to first wait for the CA server to complete updating to the new CA. Signed-off-by: Ying Li --- ca/config_test.go | 102 +++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/ca/config_test.go b/ca/config_test.go index f16a5c5735..5c28f70dac 100644 --- a/ca/config_test.go +++ b/ca/config_test.go @@ -1,6 +1,7 @@ package ca_test import ( + "bytes" "crypto/tls" "crypto/x509" "fmt" @@ -21,10 +22,11 @@ import ( "github.com/cloudflare/cfssl/helpers" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" - "github.com/docker/swarmkit/ca/testutils" + cautils "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" + "github.com/docker/swarmkit/testutils" "github.com/docker/swarmkit/watch" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -32,7 +34,7 @@ import ( ) func TestDownloadRootCASuccess(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() // Remove the CA cert @@ -60,7 +62,7 @@ func TestDownloadRootCASuccess(t *testing.T) { } func TestDownloadRootCAWrongCAHash(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() // Remove the CA cert @@ -89,10 +91,10 @@ func TestDownloadRootCAWrongCAHash(t *testing.T) { } func TestCreateSecurityConfigEmptyDir(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() assert.NoError(t, tc.CAServer.Stop()) @@ -120,7 +122,7 @@ func TestCreateSecurityConfigEmptyDir(t *testing.T) { } func TestCreateSecurityConfigNoCerts(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) @@ -162,10 +164,10 @@ func TestCreateSecurityConfigNoCerts(t *testing.T) { } func TestLoadSecurityConfigExpiredCert(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() s, err := tc.RootCA.Signer() require.NoError(t, err) @@ -179,7 +181,7 @@ func TestLoadSecurityConfigExpiredCert(t *testing.T) { require.NoError(t, err) // A cert that is not yet valid is not valid even if expiry is allowed - invalidCert := testutils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(time.Hour), now.Add(time.Hour*2)) + invalidCert := cautils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(time.Hour), now.Add(time.Hour*2)) require.NoError(t, ioutil.WriteFile(tc.Paths.Node.Cert, invalidCert, 0700)) _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) @@ -191,7 +193,7 @@ func TestLoadSecurityConfigExpiredCert(t *testing.T) { require.IsType(t, x509.CertificateInvalidError{}, errors.Cause(err)) // a cert that is expired is not valid if expiry is not allowed - invalidCert = testutils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(-2*time.Minute), now.Add(-1*time.Minute)) + invalidCert = cautils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(-2*time.Minute), now.Add(-1*time.Minute)) require.NoError(t, ioutil.WriteFile(tc.Paths.Node.Cert, invalidCert, 0700)) _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) @@ -204,10 +206,10 @@ func TestLoadSecurityConfigExpiredCert(t *testing.T) { } func TestLoadSecurityConfigInvalidCert(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() // Write some garbage to the cert @@ -222,10 +224,10 @@ some random garbage\n } func TestLoadSecurityConfigInvalidKey(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() // Write some garbage to the Key @@ -240,10 +242,10 @@ some random garbage\n } func TestLoadSecurityConfigIncorrectPassphrase(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() paths := ca.NewConfigPaths(tc.TempDir) @@ -256,7 +258,7 @@ func TestLoadSecurityConfigIncorrectPassphrase(t *testing.T) { } func TestLoadSecurityConfigIntermediates(t *testing.T) { - if testutils.External { + if cautils.External { return // this doesn't require any servers at all } tempdir, err := ioutil.TempDir("", "test-load-config-with-intermediates") @@ -265,7 +267,7 @@ func TestLoadSecurityConfigIntermediates(t *testing.T) { paths := ca.NewConfigPaths(tempdir) krw := ca.NewKeyReadWriter(paths.Node, nil, nil) - rootCA, err := ca.NewRootCA(testutils.ECDSACertChain[2], nil, nil, ca.DefaultNodeCertExpiration, nil) + rootCA, err := ca.NewRootCA(cautils.ECDSACertChain[2], nil, nil, ca.DefaultNodeCertExpiration, nil) require.NoError(t, err) ctx := log.WithLogger(context.Background(), log.L.WithFields(logrus.Fields{ @@ -274,15 +276,15 @@ func TestLoadSecurityConfigIntermediates(t *testing.T) { })) // loading the incomplete chain fails - require.NoError(t, krw.Write(testutils.ECDSACertChain[0], testutils.ECDSACertChainKeys[0], nil)) + require.NoError(t, krw.Write(cautils.ECDSACertChain[0], cautils.ECDSACertChainKeys[0], nil)) _, err = ca.LoadSecurityConfig(ctx, rootCA, krw, false) require.Error(t, err) - intermediate, err := helpers.ParseCertificatePEM(testutils.ECDSACertChain[1]) + intermediate, err := helpers.ParseCertificatePEM(cautils.ECDSACertChain[1]) require.NoError(t, err) // loading the complete chain succeeds - require.NoError(t, krw.Write(append(testutils.ECDSACertChain[0], testutils.ECDSACertChain[1]...), testutils.ECDSACertChainKeys[0], nil)) + require.NoError(t, krw.Write(append(cautils.ECDSACertChain[0], cautils.ECDSACertChain[1]...), cautils.ECDSACertChainKeys[0], nil)) secConfig, err := ca.LoadSecurityConfig(ctx, rootCA, krw, false) require.NoError(t, err) require.NotNil(t, secConfig) @@ -314,13 +316,13 @@ func TestLoadSecurityConfigIntermediates(t *testing.T) { // When the root CA is updated on the security config, the root pools are updated // and the external CA's rootCA is also updated. func TestSecurityConfigUpdateRootCA(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() tcConfig, err := tc.NewNodeConfig("worker") require.NoError(t, err) // create the "original" security config, and we'll update it to trust the test server's - cert, key, err := testutils.CreateRootCertAndKey("root1") + cert, key, err := cautils.CreateRootCertAndKey("root1") require.NoError(t, err) rootCA, err := ca.NewRootCA(cert, cert, key, ca.DefaultNodeCertExpiration, nil) @@ -370,11 +372,11 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { externalServer := tc.ExternalSigningServer tcSigner, err := tc.RootCA.Signer() require.NoError(t, err) - if testutils.External { + if cautils.External { // stop the external server and create a new one because the external server actually has to trust our client certs as well. updatedRoot, err := ca.NewRootCA(append(tc.RootCA.Certs, cert...), tcSigner.Cert, tcSigner.Key, ca.DefaultNodeCertExpiration, nil) require.NoError(t, err) - externalServer, err = testutils.NewExternalSigningServer(updatedRoot, tc.TempDir) + externalServer, err = cautils.NewExternalSigningServer(updatedRoot, tc.TempDir) require.NoError(t, err) defer externalServer.Stop() @@ -414,7 +416,7 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { // make sure any generated certs after updating contain the intermediate var generatedCert []byte - if testutils.External { + if cautils.External { // we can also now connect to the test CA's external signing server secConfig.ExternalCA().UpdateURLs(externalServer.URL) generatedCert, err = secConfig.ExternalCA().Sign(tc.Context, req) @@ -438,7 +440,7 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { // You can't update the root CA to one that doesn't match the TLS certificates func TestSecurityConfigUpdateRootCAUpdateConsistentWithTLSCertificates(t *testing.T) { t.Parallel() - if testutils.External { + if cautils.External { return // we don't care about external CAs at all } tempdir, err := ioutil.TempDir("", "") @@ -485,7 +487,7 @@ func TestSecurityConfigUpdateRootCAUpdateConsistentWithTLSCertificates(t *testin } func TestSecurityConfigSetWatch(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() secConfig, err := tc.NewNodeConfig(ca.ManagerRole) @@ -551,7 +553,7 @@ func TestRenewTLSConfigUpdatesRootOnUnknownAuthError(t *testing.T) { cas = make([]ca.RootCA, 3) ) for i := 0; i < 3; i++ { - certs[i], keys[i], err = testutils.CreateRootCertAndKey(fmt.Sprintf("CA%d", i)) + certs[i], keys[i], err = cautils.CreateRootCertAndKey(fmt.Sprintf("CA%d", i)) require.NoError(t, err) switch i { case 0: @@ -568,7 +570,7 @@ func TestRenewTLSConfigUpdatesRootOnUnknownAuthError(t *testing.T) { // the CA server is going to start off with a cert issued by the second CA, cross-signed by the first CA, and then // rotate to one issued by the third CA, cross-signed by the second. - tc := testutils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ + tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ CACert: certs[0], CAKey: keys[0], RootRotation: &api.RootRotation{ @@ -589,6 +591,24 @@ func TestRenewTLSConfigUpdatesRootOnUnknownAuthError(t *testing.T) { } return store.UpdateCluster(tx, cluster) })) + // wait until the CA is returning certs signed by the latest root + rootCA, err := ca.NewRootCA(certs[1], nil, nil, ca.DefaultNodeCertExpiration, nil) + require.NoError(t, err) + expectedIssuer, err := helpers.ParseCertificatePEM(certs[2]) + require.NoError(t, err) + require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { + _, issuerInfo, err := rootCA.RequestAndSaveNewCertificates(tc.Context, tc.KeyReadWriter, ca.CertificateRequestConfig{ + Token: tc.WorkerToken, + ConnBroker: tc.ConnBroker, + }) + if err != nil { + return err + } + if !bytes.Equal(issuerInfo.PublicKey, expectedIssuer.RawSubjectPublicKeyInfo) { + return errors.New("CA server hasn't finished updating yet") + } + return nil + }, 2*time.Second)) paths := ca.NewConfigPaths(tempdir) krw := ca.NewKeyReadWriter(paths.Node, nil, nil) @@ -657,12 +677,12 @@ func TestRenewTLSConfigUpdatesRootNonUnknownAuthError(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(tempdir) - cert, key, err := testutils.CreateRootCertAndKey("rootCA") + cert, key, err := cautils.CreateRootCertAndKey("rootCA") require.NoError(t, err) rootCA, err := ca.NewRootCA(cert, cert, key, ca.DefaultNodeCertExpiration, nil) require.NoError(t, err) - tc := testutils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ + tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ CACert: cert, CAKey: key, }, nil) @@ -689,7 +709,7 @@ func TestRenewTLSConfigUpdatesRootNonUnknownAuthError(t *testing.T) { if err != nil { return err } - node.Certificate.Certificate = testutils.ReDateCert(t, certChain, cert, key, time.Now().Add(-5*time.Hour), time.Now().Add(-4*time.Hour)) + node.Certificate.Certificate = cautils.ReDateCert(t, certChain, cert, key, time.Now().Add(-5*time.Hour), time.Now().Add(-4*time.Hour)) node.Certificate.Status = api.IssuanceStatus{ State: api.IssuanceStateIssued, } @@ -709,7 +729,7 @@ func TestRenewTLSConfigUpdatesRootNonUnknownAuthError(t *testing.T) { // enforce that no matter what order updating the root CA and updating TLS credential happens, we // end up with a security config that has updated certs, and an updated root pool func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() paths := ca.NewConfigPaths(tc.TempDir) @@ -719,11 +739,11 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { leafCert, err := ioutil.ReadFile(paths.Node.Cert) require.NoError(t, err) - cert, key, err := testutils.CreateRootCertAndKey("extra root cert for external CA") + cert, key, err := cautils.CreateRootCertAndKey("extra root cert for external CA") require.NoError(t, err) extraExternalRootCA, err := ca.NewRootCA(append(cert, tc.RootCA.Certs...), cert, key, ca.DefaultNodeCertExpiration, nil) require.NoError(t, err) - extraExternalServer, err := testutils.NewExternalSigningServer(extraExternalRootCA, tc.TempDir) + extraExternalServer, err := cautils.NewExternalSigningServer(extraExternalRootCA, tc.TempDir) require.NoError(t, err) defer extraExternalServer.Stop() secConfig.ExternalCA().UpdateURLs(extraExternalServer.URL) @@ -737,7 +757,7 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { signReq := ca.PrepareCSR(csr, "cn", ca.WorkerRole, tc.Organization) for i := 0; i < 5; i++ { - cert, _, err := testutils.CreateRootCertAndKey(fmt.Sprintf("root %d", i+2)) + cert, _, err := cautils.CreateRootCertAndKey(fmt.Sprintf("root %d", i+2)) require.NoError(t, err) ctx, cancel := context.WithCancel(tc.Context) @@ -780,7 +800,7 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { } } -func writeAlmostExpiringCertToDisk(t *testing.T, tc *testutils.TestCA, cn, ou, org string) { +func writeAlmostExpiringCertToDisk(t *testing.T, tc *cautils.TestCA, cn, ou, org string) { s, err := tc.RootCA.Signer() require.NoError(t, err) @@ -807,7 +827,7 @@ func writeAlmostExpiringCertToDisk(t *testing.T, tc *testutils.TestCA, cn, ou, o func TestRenewTLSConfigWorker(t *testing.T) { t.Parallel() - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() ctx, cancel := context.WithCancel(tc.Context) @@ -843,7 +863,7 @@ func TestRenewTLSConfigWorker(t *testing.T) { func TestRenewTLSConfigManager(t *testing.T) { t.Parallel() - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() ctx, cancel := context.WithCancel(tc.Context) @@ -879,7 +899,7 @@ func TestRenewTLSConfigManager(t *testing.T) { func TestRenewTLSConfigWithNoNode(t *testing.T) { t.Parallel() - tc := testutils.NewTestCA(t) + tc := cautils.NewTestCA(t) defer tc.Stop() ctx, cancel := context.WithCancel(tc.Context) From 8bbb8225f41de7c07dd0034830c5381da66c98e7 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Thu, 15 Jun 2017 17:56:26 -0700 Subject: [PATCH 07/12] Redact the cluster spec CA signing certificate when getting the cluster info, because otherwise getting and updating would be problematic because the key is redacted but the cert is not. Signed-off-by: Ying Li --- manager/controlapi/cluster.go | 5 +++ manager/controlapi/cluster_test.go | 57 +++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/manager/controlapi/cluster.go b/manager/controlapi/cluster.go index 9832272a3f..7e9dea2ce5 100644 --- a/manager/controlapi/cluster.go +++ b/manager/controlapi/cluster.go @@ -247,6 +247,11 @@ func redactClusters(clusters []*api.Cluster) []*api.Cluster { // Do not copy secret keys redactedSpec := cluster.Spec.Copy() redactedSpec.CAConfig.SigningCAKey = nil + // the cert is not a secret, but if API users get the cluster spec and then update, + // then because the cert is included but not the key, the user can get update errors + // or unintended consequences (such as telling swarm to forget about the key so long + // as there is a corresponding external CA) + redactedSpec.CAConfig.SigningCACert = nil redactedRootCA := cluster.RootCA.Copy() redactedRootCA.CAKey = nil diff --git a/manager/controlapi/cluster_test.go b/manager/controlapi/cluster_test.go index 372165dfe6..7df369b4ea 100644 --- a/manager/controlapi/cluster_test.go +++ b/manager/controlapi/cluster_test.go @@ -440,8 +440,8 @@ func TestUpdateClusterRotateUnlockKey(t *testing.T) { } // root rotation tests have already been covered by ca_rotation_test.go - this test only makes sure that the function tested in those -// tests is actually called by `UpdateCluster`, and that the results of GetCluster and ListCluster have the rotation root CA key -// redacted +// tests is actually called by `UpdateCluster`, and that the results of GetCluster and ListCluster have the CA keys +// and the spec key and cert redacted func TestUpdateClusterRootRotation(t *testing.T) { ts := newTestServer(t) defer ts.Stop() @@ -464,26 +464,57 @@ func TestUpdateClusterRootRotation(t *testing.T) { }) require.NoError(t, err) - var gottenClusters []*api.Cluster + checkCluster := func() *api.Cluster { + response, err = ts.Client.GetCluster(context.Background(), &api.GetClusterRequest{ClusterID: cluster.ID}) + require.NoError(t, err) + require.NotNil(t, response.Cluster) - response, err = ts.Client.GetCluster(context.Background(), &api.GetClusterRequest{ClusterID: cluster.ID}) - require.NoError(t, err) - require.NotNil(t, response.Cluster) - gottenClusters = append(gottenClusters, response.Cluster) + listResponse, err := ts.Client.ListClusters(context.Background(), &api.ListClustersRequest{}) + require.NoError(t, err) + require.Len(t, listResponse.Clusters, 1) - listResponse, err := ts.Client.ListClusters(context.Background(), &api.ListClustersRequest{}) - require.NoError(t, err) - require.Len(t, listResponse.Clusters, 1) - gottenClusters = append(gottenClusters, listResponse.Clusters[0]) + require.Equal(t, response.Cluster, listResponse.Clusters[0]) - for _, c := range gottenClusters { + c := response.Cluster require.NotNil(t, c.RootCA.RootRotation) - // check signing key redaction + // check that all keys are redacted, and that the spec signing cert is also redacted (not because + // the cert is a secret, but because that makes it easier to get-and-update) require.Len(t, c.RootCA.CAKey, 0) require.Len(t, c.RootCA.RootRotation.CAKey, 0) require.Len(t, c.Spec.CAConfig.SigningCAKey, 0) + require.Len(t, c.Spec.CAConfig.SigningCACert, 0) + + return c + } + + getUnredactedRootCA := func() (rootCA *api.RootCA) { + ts.Store.View(func(tx store.ReadTx) { + c := store.GetCluster(tx, cluster.ID) + require.NotNil(t, c) + rootCA = &c.RootCA + }) + return } + + cluster = checkCluster() + unredactedRootCA := getUnredactedRootCA() + + // update something else, but make sure this doesn't the root CA rotation doesn't change + updatedSpec = cluster.Spec.Copy() + updatedSpec.CAConfig.NodeCertExpiry = gogotypes.DurationProto(time.Hour) + _, err = ts.Client.UpdateCluster(context.Background(), &api.UpdateClusterRequest{ + ClusterID: cluster.ID, + Spec: updatedSpec, + ClusterVersion: &cluster.Meta.Version, + }) + require.NoError(t, err) + + updatedCluster := checkCluster() + require.NotEqual(t, cluster.Spec.CAConfig.NodeCertExpiry, updatedCluster.Spec.CAConfig.NodeCertExpiry) + updatedUnredactedRootCA := getUnredactedRootCA() + + require.Equal(t, unredactedRootCA, updatedUnredactedRootCA) } func TestListClusters(t *testing.T) { From a8d2a5d8434054510a030132fbaa330c46d84b41 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 11 Apr 2017 17:56:22 -0400 Subject: [PATCH 08/12] Add filtering for log plugins Docker added support for logging plugins, this makes sure swarmkit can filter nodes for task scheduling based on available log plugins. Signed-off-by: Brian Goff --- manager/scheduler/filter.go | 26 +++++-- manager/scheduler/scheduler_test.go | 101 ++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/manager/scheduler/filter.go b/manager/scheduler/filter.go index 6ce3f2c8b8..36b601c4b4 100644 --- a/manager/scheduler/filter.go +++ b/manager/scheduler/filter.go @@ -128,7 +128,7 @@ func (f *PluginFilter) SetTask(t *api.Task) bool { } } - if (c != nil && volumeTemplates) || len(t.Networks) > 0 { + if (c != nil && volumeTemplates) || len(t.Networks) > 0 || t.Spec.LogDriver != nil { f.t = t return true } @@ -153,7 +153,7 @@ func (f *PluginFilter) Check(n *NodeInfo) bool { if container != nil { for _, mount := range container.Mounts { if referencesVolumePlugin(mount) { - if !f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins) { + if _, exists := f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins); !exists { return false } } @@ -163,22 +163,34 @@ func (f *PluginFilter) Check(n *NodeInfo) bool { // Check if all network plugins required by task are installed on node for _, tn := range f.t.Networks { if tn.Network != nil && tn.Network.DriverState != nil && tn.Network.DriverState.Name != "" { - if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) { + if _, exists := f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins); !exists { return false } } } + + if f.t.Spec.LogDriver != nil { + // If there are no log driver types in the list at all, most likely this is + // an older daemon that did not report this information. In this case don't filter + if typeFound, exists := f.pluginExistsOnNode("Log", f.t.Spec.LogDriver.Name, nodePlugins); !exists && typeFound { + return false + } + } return true } // pluginExistsOnNode returns true if the (pluginName, pluginType) pair is present in nodePlugins -func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) bool { +func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) (bool, bool) { + var typeFound bool + for _, np := range nodePlugins { if pluginType != np.Type { continue } + typeFound = true + if pluginName == np.Name { - return true + return true, true } // This does not use the reference package to avoid the // overhead of parsing references as part of the scheduling @@ -186,10 +198,10 @@ func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, // strict subset of the reference grammar that is always // name:tag. if strings.HasPrefix(np.Name, pluginName) && np.Name[len(pluginName):] == ":latest" { - return true + return true, true } } - return false + return typeFound, false } // Explain returns an explanation of a failure. diff --git a/manager/scheduler/scheduler_test.go b/manager/scheduler/scheduler_test.go index 32825e1144..6846b5b02e 100644 --- a/manager/scheduler/scheduler_test.go +++ b/manager/scheduler/scheduler_test.go @@ -2178,6 +2178,10 @@ func TestSchedulerPluginConstraint(t *testing.T) { Type: "Volume", Name: "plugin1", }, + { + Type: "Log", + Name: "default", + }, }, }, }, @@ -2205,6 +2209,10 @@ func TestSchedulerPluginConstraint(t *testing.T) { Type: "Volume", Name: "plugin2", }, + { + Type: "Log", + Name: "default", + }, }, }, }, @@ -2232,6 +2240,33 @@ func TestSchedulerPluginConstraint(t *testing.T) { Type: "Network", Name: "plugin1", }, + { + Type: "Log", + Name: "default", + }, + }, + }, + }, + Status: api.NodeStatus{ + State: api.NodeStatus_READY, + }, + } + + // Node4: log plugin1 + n4 := &api.Node{ + ID: "node4_ID", + Spec: api.NodeSpec{ + Annotations: api.Annotations{ + Name: "node4", + }, + }, + Description: &api.NodeDescription{ + Engine: &api.EngineDescription{ + Plugins: []api.PluginDescription{ + { + Type: "Log", + Name: "plugin1", + }, }, }, }, @@ -2346,6 +2381,40 @@ func TestSchedulerPluginConstraint(t *testing.T) { State: api.TaskStatePending, }, } + // Task4: log plugin1 + t4 := &api.Task{ + ID: "task4_ID", + DesiredState: api.TaskStateRunning, + Spec: api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{}, + }, + LogDriver: &api.Driver{Name: "plugin1"}, + }, + ServiceAnnotations: api.Annotations{ + Name: "task4", + }, + Status: api.TaskStatus{ + State: api.TaskStatePending, + }, + } + // Task5: log plugin1 + t5 := &api.Task{ + ID: "task5_ID", + DesiredState: api.TaskStateRunning, + Spec: api.TaskSpec{ + Runtime: &api.TaskSpec_Container{ + Container: &api.ContainerSpec{}, + }, + LogDriver: &api.Driver{Name: "plugin1"}, + }, + ServiceAnnotations: api.Annotations{ + Name: "task5", + }, + Status: api.TaskStatus{ + State: api.TaskStatePending, + }, + } s := store.NewMemoryStore(nil) assert.NotNil(t, s) @@ -2418,6 +2487,38 @@ func TestSchedulerPluginConstraint(t *testing.T) { assignment2 := watchAssignment(t, watch) assert.Equal(t, assignment2.ID, "task3_ID") assert.Equal(t, assignment2.NodeID, "node3_ID") + + // Create t4; it should stay in the pending state because there is + // no node that with log plugin `plugin1` + err = s.Update(func(tx store.Tx) error { + assert.NoError(t, store.CreateTask(tx, t4)) + return nil + }) + assert.NoError(t, err) + + // check that t4 has been assigned + failure2 := watchAssignmentFailure(t, watch) + assert.Equal(t, "no suitable node (missing plugin on 3 nodes)", failure2.Status.Message) + + err = s.Update(func(tx store.Tx) error { + assert.NoError(t, store.CreateNode(tx, n4)) + return nil + }) + assert.NoError(t, err) + + // Check that t4 has been assigned + assignment3 := watchAssignment(t, watch) + assert.Equal(t, assignment3.ID, "task4_ID") + assert.Equal(t, assignment3.NodeID, "node4_ID") + + err = s.Update(func(tx store.Tx) error { + assert.NoError(t, store.CreateTask(tx, t5)) + return nil + }) + assert.NoError(t, err) + assignment4 := watchAssignment(t, watch) + assert.Equal(t, assignment4.ID, "task5_ID") + assert.Equal(t, assignment4.NodeID, "node4_ID") } func BenchmarkScheduler1kNodes1kTasks(b *testing.B) { From fa6f6a740413bddfabcba73de6c2972e73f4ac86 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Mon, 19 Jun 2017 15:35:29 -0700 Subject: [PATCH 09/12] scheduler: Change test to exercise code path where nodes are created later Signed-off-by: Aaron Lehmann --- manager/scheduler/scheduler_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/manager/scheduler/scheduler_test.go b/manager/scheduler/scheduler_test.go index 6846b5b02e..82a247ff18 100644 --- a/manager/scheduler/scheduler_test.go +++ b/manager/scheduler/scheduler_test.go @@ -2764,8 +2764,6 @@ func TestSchedulerHostPort(t *testing.T) { // Add initial node and task assert.NoError(t, store.CreateTask(tx, task1)) assert.NoError(t, store.CreateTask(tx, task2)) - assert.NoError(t, store.CreateNode(tx, node1)) - assert.NoError(t, store.CreateNode(tx, node2)) return nil }) assert.NoError(t, err) @@ -2780,6 +2778,18 @@ func TestSchedulerHostPort(t *testing.T) { }() defer scheduler.Stop() + // Tasks shouldn't be scheduled because there are no nodes. + watchAssignmentFailure(t, watch) + watchAssignmentFailure(t, watch) + + err = s.Update(func(tx store.Tx) error { + // Add initial node and task + assert.NoError(t, store.CreateNode(tx, node1)) + assert.NoError(t, store.CreateNode(tx, node2)) + return nil + }) + assert.NoError(t, err) + // Tasks 1 and 2 should be assigned to different nodes. assignment1 := watchAssignment(t, watch) assignment2 := watchAssignment(t, watch) From e2385c6116eab9fa7b6337f2d604bf31b75e5991 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Tue, 13 Jun 2017 17:11:34 -0700 Subject: [PATCH 10/12] scheduler: Clean up addOrUpdateNode This function previously could take an uninitialized NodeInfo structure and fill in whatever was missing. This is very error-prone, so remove this logic and change the only caller that relies on it to always pass in a properly initialized NodeInfo. Signed-off-by: Aaron Lehmann --- manager/scheduler/nodeset.go | 11 ----------- manager/scheduler/scheduler.go | 25 ++++++++++++++++--------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/manager/scheduler/nodeset.go b/manager/scheduler/nodeset.go index 7f899d8b26..b83704a18d 100644 --- a/manager/scheduler/nodeset.go +++ b/manager/scheduler/nodeset.go @@ -4,7 +4,6 @@ import ( "container/heap" "errors" "strings" - "time" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/constraint" @@ -32,16 +31,6 @@ func (ns *nodeSet) nodeInfo(nodeID string) (NodeInfo, error) { // addOrUpdateNode sets the number of tasks for a given node. It adds the node // to the set if it wasn't already tracked. func (ns *nodeSet) addOrUpdateNode(n NodeInfo) { - if n.Tasks == nil { - n.Tasks = make(map[string]*api.Task) - } - if n.ActiveTasksCountByService == nil { - n.ActiveTasksCountByService = make(map[string]int) - } - if n.recentFailures == nil { - n.recentFailures = make(map[string][]time.Time) - } - ns.nodes[n.ID] = n } diff --git a/manager/scheduler/scheduler.go b/manager/scheduler/scheduler.go index 9ff921fd5b..07d9b0458c 100644 --- a/manager/scheduler/scheduler.go +++ b/manager/scheduler/scheduler.go @@ -315,25 +315,32 @@ func (s *Scheduler) deleteTask(t *api.Task) bool { } func (s *Scheduler) createOrUpdateNode(n *api.Node) { - nodeInfo, _ := s.nodeSet.nodeInfo(n.ID) + nodeInfo, nodeInfoErr := s.nodeSet.nodeInfo(n.ID) var resources *api.Resources if n.Description != nil && n.Description.Resources != nil { resources = n.Description.Resources.Copy() // reconcile resources by looping over all tasks in this node - for _, task := range nodeInfo.Tasks { - reservations := taskReservations(task.Spec) + if nodeInfoErr == nil { + for _, task := range nodeInfo.Tasks { + reservations := taskReservations(task.Spec) - resources.MemoryBytes -= reservations.MemoryBytes - resources.NanoCPUs -= reservations.NanoCPUs + resources.MemoryBytes -= reservations.MemoryBytes + resources.NanoCPUs -= reservations.NanoCPUs - genericresource.ConsumeNodeResources(&resources.Generic, - task.AssignedGenericResources) + genericresource.ConsumeNodeResources(&resources.Generic, + task.AssignedGenericResources) + } } } else { resources = &api.Resources{} } - nodeInfo.Node = n - nodeInfo.AvailableResources = resources + + if nodeInfoErr != nil { + nodeInfo = newNodeInfo(n, nil, *resources) + } else { + nodeInfo.Node = n + nodeInfo.AvailableResources = resources + } s.nodeSet.addOrUpdateNode(nodeInfo) } From 0907ae545631790d997fc26c53ed837393f04ae5 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Thu, 15 Jun 2017 22:42:58 -0700 Subject: [PATCH 11/12] Use the TestCA context in all the manager tests in manager_test.go, so that we can log the test name in the output. Signed-off-by: Ying Li --- manager/manager_test.go | 48 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/manager/manager_test.go b/manager/manager_test.go index bef92df0a3..b6b8820331 100644 --- a/manager/manager_test.go +++ b/manager/manager_test.go @@ -31,8 +31,6 @@ import ( ) func TestManager(t *testing.T) { - ctx := context.Background() - temp, err := ioutil.TempFile("", "test-socket") assert.NoError(t, err) assert.NoError(t, temp.Close()) @@ -73,7 +71,7 @@ func TestManager(t *testing.T) { done := make(chan error) defer close(done) go func() { - done <- m.Run(ctx) + done <- m.Run(tc.Context) }() opts := []grpc.DialOption{ @@ -89,9 +87,9 @@ func TestManager(t *testing.T) { // We have to send a dummy request to verify if the connection is actually up. client := api.NewDispatcherClient(conn) - _, err = client.Heartbeat(ctx, &api.HeartbeatRequest{}) + _, err = client.Heartbeat(tc.Context, &api.HeartbeatRequest{}) assert.Equal(t, dispatcher.ErrNodeNotRegistered.Error(), grpc.ErrorDesc(err)) - _, err = client.Session(ctx, &api.SessionRequest{}) + _, err = client.Session(tc.Context, &api.SessionRequest{}) assert.NoError(t, err) // Try to have a client in a different org access this manager @@ -211,7 +209,7 @@ func TestManager(t *testing.T) { _, err = client.Heartbeat(context.Background(), &api.HeartbeatRequest{}) assert.Contains(t, grpc.ErrorDesc(err), "removed from swarm") - m.Stop(ctx, false) + m.Stop(tc.Context, false) // After stopping we should MAY receive an error from ListenAndServe if // all this happened before WaitForLeader completed, so don't check the @@ -221,8 +219,6 @@ func TestManager(t *testing.T) { // Tests locking and unlocking the manager and key rotations func TestManagerLockUnlock(t *testing.T) { - ctx := context.Background() - temp, err := ioutil.TempFile("", "test-manager-lock") require.NoError(t, err) require.NoError(t, temp.Close()) @@ -257,7 +253,7 @@ func TestManagerLockUnlock(t *testing.T) { done := make(chan error) defer close(done) go func() { - done <- m.Run(ctx) + done <- m.Run(tc.Context) }() opts := []grpc.DialOption{ @@ -277,7 +273,7 @@ func TestManagerLockUnlock(t *testing.T) { client := api.NewControlClient(conn) require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { - resp, err := client.ListClusters(ctx, &api.ListClustersRequest{}) + resp, err := client.ListClusters(tc.Context, &api.ListClustersRequest{}) if err != nil { return err } @@ -303,13 +299,13 @@ func TestManagerLockUnlock(t *testing.T) { // update the lock key - this may fail due to update out of sequence errors, so try again for { - getResp, err := client.GetCluster(ctx, &api.GetClusterRequest{ClusterID: cluster.ID}) + getResp, err := client.GetCluster(tc.Context, &api.GetClusterRequest{ClusterID: cluster.ID}) require.NoError(t, err) cluster = getResp.Cluster spec := cluster.Spec.Copy() spec.EncryptionConfig.AutoLockManagers = true - updateResp, err := client.UpdateCluster(ctx, &api.UpdateClusterRequest{ + updateResp, err := client.UpdateCluster(tc.Context, &api.UpdateClusterRequest{ ClusterID: cluster.ID, ClusterVersion: &cluster.Meta.Version, Spec: spec, @@ -326,7 +322,7 @@ func TestManagerLockUnlock(t *testing.T) { require.NoError(t, err) caConn := api.NewCAClient(conn) - unlockKeyResp, err := caConn.GetUnlockKey(ctx, &api.GetUnlockKeyRequest{}) + unlockKeyResp, err := caConn.GetUnlockKey(tc.Context, &api.GetUnlockKeyRequest{}) require.NoError(t, err) // this should update the TLS key, rotate the DEK, and finish snapshotting @@ -377,13 +373,13 @@ func TestManagerLockUnlock(t *testing.T) { // update the lock key to nil for i := 0; i < 3; i++ { - getResp, err := client.GetCluster(ctx, &api.GetClusterRequest{ClusterID: cluster.ID}) + getResp, err := client.GetCluster(tc.Context, &api.GetClusterRequest{ClusterID: cluster.ID}) require.NoError(t, err) cluster = getResp.Cluster spec := cluster.Spec.Copy() spec.EncryptionConfig.AutoLockManagers = false - _, err = client.UpdateCluster(ctx, &api.UpdateClusterRequest{ + _, err = client.UpdateCluster(tc.Context, &api.UpdateClusterRequest{ ClusterID: cluster.ID, ClusterVersion: &cluster.Meta.Version, Spec: spec, @@ -420,7 +416,7 @@ func TestManagerLockUnlock(t *testing.T) { require.NotNil(t, unencryptedDEK) require.Equal(t, currentDEK, unencryptedDEK) - m.Stop(ctx, false) + m.Stop(tc.Context, false) // After stopping we should MAY receive an error from ListenAndServe if // all this happened before WaitForLeader completed, so don't check the @@ -431,8 +427,6 @@ func TestManagerLockUnlock(t *testing.T) { // If the root CA material is updated in the memory store, a manager will update its own // security configs even if it's "not the leader" (which we will fake by calling `becomeFollower`) func TestManagerUpdatesSecurityConfig(t *testing.T) { - ctx := context.Background() - temp, err := ioutil.TempFile("", "test-manager-update-security-config") require.NoError(t, err) require.NoError(t, temp.Close()) @@ -466,7 +460,7 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { done := make(chan error) defer close(done) go func() { - done <- m.Run(ctx) + done <- m.Run(tc.Context) }() // wait until the CA server is running @@ -484,7 +478,7 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { client := api.NewCAClient(conn) require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { - ctx, _ := context.WithTimeout(context.Background(), 500*time.Millisecond) + ctx, _ := context.WithTimeout(tc.Context, 500*time.Millisecond) _, err := client.GetRootCACertificate(ctx, &api.GetRootCACertificateRequest{}) return err }, time.Second)) @@ -530,7 +524,7 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { return nil }, 1*time.Second)) - m.Stop(ctx, false) + m.Stop(tc.Context, false) // After stopping we should MAY receive an error from ListenAndServe if // all this happened before WaitForLeader completed, so don't check the @@ -540,8 +534,6 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { // Tests manager rotates encryption of root key data in the raft store func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { - ctx := context.Background() - tc := cautils.NewTestCA(t) defer tc.Stop() @@ -579,7 +571,7 @@ func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { require.NotNil(t, m) go func() { - done <- m.Run(ctx) + done <- m.Run(tc.Context) }() } @@ -606,7 +598,7 @@ func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { defer os.Unsetenv(ca.PassphraseENVVar) // restart - m.Stop(ctx, false) + m.Stop(tc.Context, false) <-done startManager() @@ -635,7 +627,7 @@ func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { defer os.Unsetenv(ca.PassphraseENVVarPrev) // restart - m.Stop(ctx, false) + m.Stop(tc.Context, false) <-done startManager() @@ -687,11 +679,11 @@ G80TfNRRr/qdB9hLwfyOyk2tBipkAgs6cl+CZAaqx3k= })) // restart - m.Stop(ctx, false) + m.Stop(tc.Context, false) <-done startManager() require.NoError(t, pollDecrypted()) - m.Stop(ctx, false) + m.Stop(tc.Context, false) <-done } From 04955ca7e1d6845d82902ff3b3767ce866f12275 Mon Sep 17 00:00:00 2001 From: Pradip Dhara Date: Tue, 20 Jun 2017 12:51:51 -0700 Subject: [PATCH 12/12] Enable swarm service on node-local network (l2bridge) on windows. Signed-off-by: Pradip Dhara --- manager/allocator/cnmallocator/drivers_network_windows.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/manager/allocator/cnmallocator/drivers_network_windows.go b/manager/allocator/cnmallocator/drivers_network_windows.go index 8cbedbd6b8..e383b60705 100644 --- a/manager/allocator/cnmallocator/drivers_network_windows.go +++ b/manager/allocator/cnmallocator/drivers_network_windows.go @@ -3,12 +3,17 @@ package cnmallocator import ( "github.com/docker/libnetwork/drivers/overlay/ovmanager" "github.com/docker/libnetwork/drivers/remote" + "github.com/docker/libnetwork/drivers/windows" "github.com/docker/swarmkit/manager/allocator/networkallocator" ) var initializers = []initializer{ {remote.Init, "remote"}, {ovmanager.Init, "overlay"}, + {windows.GetInit("transparent"), "transparent"}, + {windows.GetInit("l2bridge"), "l2bridge"}, + {windows.GetInit("l2tunnel"), "l2tunnel"}, + {windows.GetInit("nat"), "nat"}, } // PredefinedNetworks returns the list of predefined network structures