Merge pull request #315 from Yan0613/master
[feature] enable zero trust security
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4e75804..32e9cca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,10 +38,10 @@
# golang:
# - '1.21'
# steps:
-# - uses: actions/setup-go@v3
+# - uses: actions/setup-go@v5
# with:
# go-version: ${{ matrix.go_version }}
-# - uses: actions/checkout@v3
+# - uses: actions/checkout@v4
# - name: golangci-lint
# uses: golangci/golangci-lint-action@v3.4.0
# with:
@@ -56,7 +56,7 @@
steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x
- uses: actions/setup-go@v4
+ uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Download dependencies
@@ -67,7 +67,7 @@
go test ./... -gcflags=-l -coverprofile=coverage.txt -covermode=atomic
- name: "Upload test result"
if: always()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: test-coverage
path: "**/coverage.txt"
@@ -81,7 +81,7 @@
steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x
- uses: actions/setup-go@v4
+ uses: actions/setup-go@v5
with:
go-version-file: go.mod
@@ -95,7 +95,7 @@
steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x
- uses: actions/setup-go@v4
+ uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Download dependencies
diff --git a/.github/workflows/dubbo-release.yaml b/.github/workflows/dubbo-release.yaml
index b25ea64..8bd682f 100644
--- a/.github/workflows/dubbo-release.yaml
+++ b/.github/workflows/dubbo-release.yaml
@@ -41,7 +41,7 @@
steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x
- uses: actions/setup-go@v4
+ uses: actions/setup-go@v5
with:
go-version-file: go.mod
diff --git a/.github/workflows/dubboctl-release.yaml b/.github/workflows/dubboctl-release.yaml
index e4d1468..b1c9e92 100644
--- a/.github/workflows/dubboctl-release.yaml
+++ b/.github/workflows/dubboctl-release.yaml
@@ -41,7 +41,7 @@
steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x
- uses: actions/setup-go@v4
+ uses: actions/setup-go@v5
with:
go-version-file: go.mod
diff --git a/.gitignore b/.gitignore
index 08c1ddb..49e7864 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,6 @@
bin/
build/
-vendor/
\ No newline at end of file
+vendor/
+
+dockercompose.yaml
\ No newline at end of file
diff --git a/api/common/v1alpha1/tls/tls.go b/api/common/v1alpha1/tls/tls.go
new file mode 100644
index 0000000..99df9c7
--- /dev/null
+++ b/api/common/v1alpha1/tls/tls.go
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// +kubebuilder:object:generate=true
+package tls
+
+import (
+ "slices"
+
+ tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+)
+
+// +kubebuilder:validation:Enum=TLSAuto;TLS10;TLS11;TLS12;TLS13
+type TlsVersion string
+
+const (
+ TLSVersionAuto TlsVersion = "TLSAuto"
+ TLSVersion10 TlsVersion = "TLS10"
+ TLSVersion11 TlsVersion = "TLS11"
+ TLSVersion12 TlsVersion = "TLS12"
+ TLSVersion13 TlsVersion = "TLS13"
+)
+
+var TlsVersionOrder = map[TlsVersion]int{
+ TLSVersion10: 0,
+ TLSVersion11: 1,
+ TLSVersion12: 2,
+ TLSVersion13: 3,
+}
+
+var allTlsVersions = []string{string(TLSVersionAuto), string(TLSVersion10), string(TLSVersion11), string(TLSVersion12), string(TLSVersion13)}
+
+type Version struct {
+ // Min defines minimum supported version. One of `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`.
+ // +kubebuilder:default=TLSAuto
+ Min *TlsVersion `json:"min,omitempty"`
+ // Max defines maximum supported version. One of `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`.
+ // +kubebuilder:default=TLSAuto
+ Max *TlsVersion `json:"max,omitempty"`
+}
+
+func ValidateVersion(version *Version) validators.ValidationError {
+ var verr validators.ValidationError
+ path := validators.Root()
+ specificMin := false
+ specificMax := false
+ if version.Min != nil {
+ if !slices.Contains(allTlsVersions, string(*version.Min)) {
+ verr.AddErrorAt(path.Field("min"), validators.MakeFieldMustBeOneOfErr("min", allTlsVersions...))
+ } else if *version.Min != TLSVersionAuto {
+ specificMin = true
+ }
+ }
+ if version.Max != nil {
+ if !slices.Contains(allTlsVersions, string(*version.Max)) {
+ verr.AddErrorAt(path.Field("max"), validators.MakeFieldMustBeOneOfErr("max", allTlsVersions...))
+ } else if *version.Max != TLSVersionAuto {
+ specificMax = true
+ }
+ }
+
+ if specificMin && specificMax && TlsVersionOrder[*version.Min] > TlsVersionOrder[*version.Max] {
+ verr.AddViolationAt(path.Field("min"), "min version must be lower than max")
+ }
+
+ return verr
+}
+
+func ToTlsVersion(version *TlsVersion) tlsv3.TlsParameters_TlsProtocol {
+ switch *version {
+ case TLSVersion13:
+ return tlsv3.TlsParameters_TLSv1_3
+ case TLSVersion12:
+ return tlsv3.TlsParameters_TLSv1_2
+ case TLSVersion11:
+ return tlsv3.TlsParameters_TLSv1_1
+ case TLSVersion10:
+ return tlsv3.TlsParameters_TLSv1_0
+ case TLSVersionAuto:
+ fallthrough
+ default:
+ return tlsv3.TlsParameters_TLS_AUTO
+ }
+}
+
+// +kubebuilder:validation:Enum=ECDHE-ECDSA-AES128-GCM-SHA256;ECDHE-ECDSA-AES256-GCM-SHA384;ECDHE-ECDSA-CHACHA20-POLY1305;ECDHE-RSA-AES128-GCM-SHA256;ECDHE-RSA-AES256-GCM-SHA384;ECDHE-RSA-CHACHA20-POLY1305
+type TlsCipher string
+
+const (
+ EcdheEcdsaAes128GcmSha256 TlsCipher = "ECDHE-ECDSA-AES128-GCM-SHA256"
+ EcdheEcdsaAes256GcmSha384 TlsCipher = "ECDHE-ECDSA-AES256-GCM-SHA384"
+ EcdheEcdsaChacha20Poly1305 TlsCipher = "ECDHE-ECDSA-CHACHA20-POLY1305"
+ EcdheRsaAes128GcmSha256 TlsCipher = "ECDHE-RSA-AES128-GCM-SHA256"
+ EcdheRsaAes256GcmSha384 TlsCipher = "ECDHE-RSA-AES256-GCM-SHA384"
+ EcdheRsaChacha20Poly1305 TlsCipher = "ECDHE-RSA-CHACHA20-POLY1305"
+)
+
+var AllCiphers = []string{
+ string(EcdheEcdsaAes128GcmSha256),
+ string(EcdheEcdsaAes256GcmSha384),
+ string(EcdheEcdsaChacha20Poly1305),
+ string(EcdheRsaAes128GcmSha256),
+ string(EcdheRsaAes256GcmSha384),
+ string(EcdheRsaChacha20Poly1305),
+}
+
+type TlsCiphers []TlsCipher
diff --git a/api/common/v1alpha1/tls/zz_generated.deepcopy.go b/api/common/v1alpha1/tls/zz_generated.deepcopy.go
new file mode 100644
index 0000000..7f73f6c
--- /dev/null
+++ b/api/common/v1alpha1/tls/zz_generated.deepcopy.go
@@ -0,0 +1,51 @@
+//go:build !ignore_autogenerated
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package tls
+
+import ()
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in TlsCiphers) DeepCopyInto(out *TlsCiphers) {
+ {
+ in := &in
+ *out = make(TlsCiphers, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TlsCiphers.
+func (in TlsCiphers) DeepCopy() TlsCiphers {
+ if in == nil {
+ return nil
+ }
+ out := new(TlsCiphers)
+ in.DeepCopyInto(out)
+ return *out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Version) DeepCopyInto(out *Version) {
+ *out = *in
+ if in.Min != nil {
+ in, out := &in.Min, &out.Min
+ *out = new(TlsVersion)
+ **out = **in
+ }
+ if in.Max != nil {
+ in, out := &in.Max, &out.Max
+ *out = new(TlsVersion)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Version.
+func (in *Version) DeepCopy() *Version {
+ if in == nil {
+ return nil
+ }
+ out := new(Version)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/api/mesh/options.pb.go b/api/mesh/options.pb.go
index da895ec..a738fc9 100644
--- a/api/mesh/options.pb.go
+++ b/api/mesh/options.pb.go
@@ -1,24 +1,19 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.28.1
-// protoc v3.20.0
-// source: api/mesh/options.proto
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: options.proto
package mesh
import (
+ descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
- descriptorpb "google.golang.org/protobuf/types/descriptorpb"
-)
-
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -67,7 +62,7 @@
func (x *DubboResourceOptions) Reset() {
*x = DubboResourceOptions{}
if protoimpl.UnsafeEnabled {
- mi := &file_api_mesh_options_proto_msgTypes[0]
+ mi := &file_options_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -80,7 +75,7 @@
func (*DubboResourceOptions) ProtoMessage() {}
func (x *DubboResourceOptions) ProtoReflect() protoreflect.Message {
- mi := &file_api_mesh_options_proto_msgTypes[0]
+ mi := &file_options_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -93,7 +88,7 @@
// Deprecated: Use DubboResourceOptions.ProtoReflect.Descriptor instead.
func (*DubboResourceOptions) Descriptor() ([]byte, []int) {
- return file_api_mesh_options_proto_rawDescGZIP(), []int{0}
+ return file_options_proto_rawDescGZIP(), []int{0}
}
func (x *DubboResourceOptions) GetName() string {
@@ -221,7 +216,7 @@
func (x *DubboWsOptions) Reset() {
*x = DubboWsOptions{}
if protoimpl.UnsafeEnabled {
- mi := &file_api_mesh_options_proto_msgTypes[1]
+ mi := &file_options_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -234,7 +229,7 @@
func (*DubboWsOptions) ProtoMessage() {}
func (x *DubboWsOptions) ProtoReflect() protoreflect.Message {
- mi := &file_api_mesh_options_proto_msgTypes[1]
+ mi := &file_options_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -247,7 +242,7 @@
// Deprecated: Use DubboWsOptions.ProtoReflect.Descriptor instead.
func (*DubboWsOptions) Descriptor() ([]byte, []int) {
- return file_api_mesh_options_proto_rawDescGZIP(), []int{1}
+ return file_options_proto_rawDescGZIP(), []int{1}
}
func (x *DubboWsOptions) GetName() string {
@@ -292,7 +287,7 @@
func (x *DubboDdsOptions) Reset() {
*x = DubboDdsOptions{}
if protoimpl.UnsafeEnabled {
- mi := &file_api_mesh_options_proto_msgTypes[2]
+ mi := &file_options_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -305,7 +300,7 @@
func (*DubboDdsOptions) ProtoMessage() {}
func (x *DubboDdsOptions) ProtoReflect() protoreflect.Message {
- mi := &file_api_mesh_options_proto_msgTypes[2]
+ mi := &file_options_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -318,7 +313,7 @@
// Deprecated: Use DubboDdsOptions.ProtoReflect.Descriptor instead.
func (*DubboDdsOptions) Descriptor() ([]byte, []int) {
- return file_api_mesh_options_proto_rawDescGZIP(), []int{2}
+ return file_options_proto_rawDescGZIP(), []int{2}
}
func (x *DubboDdsOptions) GetSendToGlobal() bool {
@@ -350,7 +345,7 @@
func (x *DubboPolicyOptions) Reset() {
*x = DubboPolicyOptions{}
if protoimpl.UnsafeEnabled {
- mi := &file_api_mesh_options_proto_msgTypes[3]
+ mi := &file_options_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -363,7 +358,7 @@
func (*DubboPolicyOptions) ProtoMessage() {}
func (x *DubboPolicyOptions) ProtoReflect() protoreflect.Message {
- mi := &file_api_mesh_options_proto_msgTypes[3]
+ mi := &file_options_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -376,7 +371,7 @@
// Deprecated: Use DubboPolicyOptions.ProtoReflect.Descriptor instead.
func (*DubboPolicyOptions) Descriptor() ([]byte, []int) {
- return file_api_mesh_options_proto_rawDescGZIP(), []int{3}
+ return file_options_proto_rawDescGZIP(), []int{3}
}
func (x *DubboPolicyOptions) GetSkipRegistration() bool {
@@ -393,137 +388,136 @@
return ""
}
-var file_api_mesh_options_proto_extTypes = []protoimpl.ExtensionInfo{
+var file_options_proto_extTypes = []protoimpl.ExtensionInfo{
{
- ExtendedType: (*descriptorpb.MessageOptions)(nil),
+ ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*DubboResourceOptions)(nil),
Field: 43534533,
Name: "dubbo.mesh.resource",
Tag: "bytes,43534533,opt,name=resource",
- Filename: "api/mesh/options.proto",
+ Filename: "options.proto",
},
{
- ExtendedType: (*descriptorpb.MessageOptions)(nil),
+ ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*DubboPolicyOptions)(nil),
Field: 43534534,
Name: "dubbo.mesh.policy",
Tag: "bytes,43534534,opt,name=policy",
- Filename: "api/mesh/options.proto",
+ Filename: "options.proto",
},
}
-// Extension fields to descriptorpb.MessageOptions.
+// Extension fields to descriptor.MessageOptions.
var (
// optional dubbo.mesh.DubboResourceOptions resource = 43534533;
- E_Resource = &file_api_mesh_options_proto_extTypes[0] // 'dubbo'
+ E_Resource = &file_options_proto_extTypes[0] // 'dubbo'
// optional dubbo.mesh.DubboPolicyOptions policy = 43534534;
- E_Policy = &file_api_mesh_options_proto_extTypes[1] // 'dubbo'
+ E_Policy = &file_options_proto_extTypes[1] // 'dubbo'
)
-var File_api_mesh_options_proto protoreflect.FileDescriptor
+var File_options_proto protoreflect.FileDescriptor
-var file_api_mesh_options_proto_rawDesc = []byte{
- 0x0a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
- 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
- 0x6d, 0x65, 0x73, 0x68, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe8, 0x04, 0x0a, 0x14, 0x44, 0x75, 0x62, 0x62, 0x6f,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
- 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61,
- 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12,
- 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69,
- 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
- 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x03, 0x64, 0x64, 0x73, 0x18, 0x0a, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
- 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 0x52, 0x03, 0x64, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x02, 0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44,
- 0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x02, 0x77,
- 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73,
- 0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x63, 0x6f, 0x70,
- 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x6b,
- 0x69, 0x70, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72,
- 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x6b,
- 0x69, 0x70, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x57, 0x72, 0x61, 0x70,
- 0x70, 0x65, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x6f,
- 0x5f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
- 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x27,
- 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
- 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
- 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x6c, 0x75, 0x72, 0x61,
- 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70,
- 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x65, 0x78,
- 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x0e, 0x69, 0x73, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c,
- 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70,
- 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x11,
- 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
- 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x21,
- 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x12,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74,
- 0x73, 0x22, 0x78, 0x0a, 0x0e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61,
- 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x12,
- 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a,
- 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x59, 0x0a, 0x0f, 0x44,
- 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24,
- 0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x47, 0x6c,
- 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f,
- 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64,
- 0x54, 0x6f, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0x59, 0x0a, 0x12, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50,
- 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11,
- 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67,
- 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75,
- 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61,
- 0x6c, 0x3a, 0x60, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc5,
- 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
- 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x3a, 0x5a, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc6,
- 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
- 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
- 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42,
- 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70,
- 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+var file_options_proto_rawDesc = []byte{
+ 0x0a, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+ 0x0a, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x1a, 0x20, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe8, 0x04,
+ 0x0a, 0x14, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
+ 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
+ 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+ 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+ 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69,
+ 0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
+ 0x03, 0x64, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x75, 0x62,
+ 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73,
+ 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x03, 0x64, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x02,
+ 0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f,
+ 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x02, 0x77, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70,
+ 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x6b, 0x69, 0x70, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x65, 0x73, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61,
+ 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18,
+ 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x49, 0x6e,
+ 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
+ 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
+ 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e,
+ 0x0a, 0x13, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
+ 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75,
+ 0x72, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27,
+ 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+ 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x45, 0x78, 0x70, 0x65, 0x72,
+ 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f,
+ 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x61, 0x64, 0x64,
+ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f,
+ 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x73,
+ 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73,
+ 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x22, 0x78, 0x0a, 0x0e, 0x44, 0x75, 0x62, 0x62,
+ 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
+ 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+ 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f,
+ 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f,
+ 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4f, 0x6e,
+ 0x6c, 0x79, 0x22, 0x59, 0x0a, 0x0f, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f,
+ 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73,
+ 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x73,
+ 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0x59, 0x0a,
+ 0x12, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69,
+ 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+ 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x3a, 0x60, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc5, 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
+ 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62,
+ 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x5a, 0x0a, 0x06, 0x70, 0x6f,
+ 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc6, 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
+ 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62,
+ 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62,
+ 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69,
+ 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
- file_api_mesh_options_proto_rawDescOnce sync.Once
- file_api_mesh_options_proto_rawDescData = file_api_mesh_options_proto_rawDesc
+ file_options_proto_rawDescOnce sync.Once
+ file_options_proto_rawDescData = file_options_proto_rawDesc
)
-func file_api_mesh_options_proto_rawDescGZIP() []byte {
- file_api_mesh_options_proto_rawDescOnce.Do(func() {
- file_api_mesh_options_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_options_proto_rawDescData)
+func file_options_proto_rawDescGZIP() []byte {
+ file_options_proto_rawDescOnce.Do(func() {
+ file_options_proto_rawDescData = protoimpl.X.CompressGZIP(file_options_proto_rawDescData)
})
- return file_api_mesh_options_proto_rawDescData
+ return file_options_proto_rawDescData
}
-var file_api_mesh_options_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_api_mesh_options_proto_goTypes = []interface{}{
- (*DubboResourceOptions)(nil), // 0: dubbo.mesh.DubboResourceOptions
- (*DubboWsOptions)(nil), // 1: dubbo.mesh.DubboWsOptions
- (*DubboDdsOptions)(nil), // 2: dubbo.mesh.DubboDdsOptions
- (*DubboPolicyOptions)(nil), // 3: dubbo.mesh.DubboPolicyOptions
- (*descriptorpb.MessageOptions)(nil), // 4: google.protobuf.MessageOptions
+var file_options_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_options_proto_goTypes = []interface{}{
+ (*DubboResourceOptions)(nil), // 0: dubbo.mesh.DubboResourceOptions
+ (*DubboWsOptions)(nil), // 1: dubbo.mesh.DubboWsOptions
+ (*DubboDdsOptions)(nil), // 2: dubbo.mesh.DubboDdsOptions
+ (*DubboPolicyOptions)(nil), // 3: dubbo.mesh.DubboPolicyOptions
+ (*descriptor.MessageOptions)(nil), // 4: google.protobuf.MessageOptions
}
-var file_api_mesh_options_proto_depIdxs = []int32{
+var file_options_proto_depIdxs = []int32{
2, // 0: dubbo.mesh.DubboResourceOptions.dds:type_name -> dubbo.mesh.DubboDdsOptions
1, // 1: dubbo.mesh.DubboResourceOptions.ws:type_name -> dubbo.mesh.DubboWsOptions
4, // 2: dubbo.mesh.resource:extendee -> google.protobuf.MessageOptions
@@ -537,13 +531,13 @@
0, // [0:2] is the sub-list for field type_name
}
-func init() { file_api_mesh_options_proto_init() }
-func file_api_mesh_options_proto_init() {
- if File_api_mesh_options_proto != nil {
+func init() { file_options_proto_init() }
+func file_options_proto_init() {
+ if File_options_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
- file_api_mesh_options_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ file_options_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DubboResourceOptions); i {
case 0:
return &v.state
@@ -555,7 +549,7 @@
return nil
}
}
- file_api_mesh_options_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ file_options_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DubboWsOptions); i {
case 0:
return &v.state
@@ -567,7 +561,7 @@
return nil
}
}
- file_api_mesh_options_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ file_options_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DubboDdsOptions); i {
case 0:
return &v.state
@@ -579,7 +573,7 @@
return nil
}
}
- file_api_mesh_options_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ file_options_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DubboPolicyOptions); i {
case 0:
return &v.state
@@ -596,19 +590,19 @@
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_api_mesh_options_proto_rawDesc,
+ RawDescriptor: file_options_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 2,
NumServices: 0,
},
- GoTypes: file_api_mesh_options_proto_goTypes,
- DependencyIndexes: file_api_mesh_options_proto_depIdxs,
- MessageInfos: file_api_mesh_options_proto_msgTypes,
- ExtensionInfos: file_api_mesh_options_proto_extTypes,
+ GoTypes: file_options_proto_goTypes,
+ DependencyIndexes: file_options_proto_depIdxs,
+ MessageInfos: file_options_proto_msgTypes,
+ ExtensionInfos: file_options_proto_extTypes,
}.Build()
- File_api_mesh_options_proto = out.File
- file_api_mesh_options_proto_rawDesc = nil
- file_api_mesh_options_proto_goTypes = nil
- file_api_mesh_options_proto_depIdxs = nil
+ File_options_proto = out.File
+ file_options_proto_rawDesc = nil
+ file_options_proto_goTypes = nil
+ file_options_proto_depIdxs = nil
}
diff --git a/api/mesh/v1alpha1/dds.proto b/api/mesh/v1alpha1/dds.proto
index 6bd6f04..aab133b 100644
--- a/api/mesh/v1alpha1/dds.proto
+++ b/api/mesh/v1alpha1/dds.proto
@@ -3,7 +3,6 @@
package dubbo.mesh.v1alpha1;
option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
-
import "envoy/service/discovery/v3/discovery.proto";
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
diff --git a/api/mesh/v1alpha1/mesh.pb.go b/api/mesh/v1alpha1/mesh.pb.go
index b6926cf..9cb07e5 100644
--- a/api/mesh/v1alpha1/mesh.pb.go
+++ b/api/mesh/v1alpha1/mesh.pb.go
@@ -1,29 +1,22 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.28.1
-// protoc v3.20.0
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
// source: api/mesh/v1alpha1/mesh.proto
package v1alpha1
import (
+ _ "github.com/apache/dubbo-kubernetes/api/mesh"
+ duration "github.com/golang/protobuf/ptypes/duration"
+ _struct "github.com/golang/protobuf/ptypes/struct"
+ wrappers "github.com/golang/protobuf/ptypes/wrappers"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
- structpb "google.golang.org/protobuf/types/known/structpb"
- wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
-)
-
-import (
- _ "github.com/apache/dubbo-kubernetes/api/mesh"
-)
-
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -31,6 +24,58 @@
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
+type CertificateAuthorityBackend_Mode int32
+
+const (
+ // A STRICT mode implies that the server validates the connection and
+ // accepts only encrypted TLS traffic
+ CertificateAuthorityBackend_STRICT CertificateAuthorityBackend_Mode = 0
+ // A PERMISSIVE mode implies that the outbounds encrypt traffic the same way
+ // it happens in strict mode, but inbounds accept both TLS and plaintext
+ // traffic. This allows applications residing in the mesh to accept requests
+ // from outside of the mesh.
+ CertificateAuthorityBackend_PERMISSIVE CertificateAuthorityBackend_Mode = 1
+)
+
+// Enum value maps for CertificateAuthorityBackend_Mode.
+var (
+ CertificateAuthorityBackend_Mode_name = map[int32]string{
+ 0: "STRICT",
+ 1: "PERMISSIVE",
+ }
+ CertificateAuthorityBackend_Mode_value = map[string]int32{
+ "STRICT": 0,
+ "PERMISSIVE": 1,
+ }
+)
+
+func (x CertificateAuthorityBackend_Mode) Enum() *CertificateAuthorityBackend_Mode {
+ p := new(CertificateAuthorityBackend_Mode)
+ *p = x
+ return p
+}
+
+func (x CertificateAuthorityBackend_Mode) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CertificateAuthorityBackend_Mode) Descriptor() protoreflect.EnumDescriptor {
+ return file_api_mesh_v1alpha1_mesh_proto_enumTypes[0].Descriptor()
+}
+
+func (CertificateAuthorityBackend_Mode) Type() protoreflect.EnumType {
+ return &file_api_mesh_v1alpha1_mesh_proto_enumTypes[0]
+}
+
+func (x CertificateAuthorityBackend_Mode) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use CertificateAuthorityBackend_Mode.Descriptor instead.
+func (CertificateAuthorityBackend_Mode) EnumDescriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{1, 0}
+}
+
// Mesh defines configuration of a single mesh.
type Mesh struct {
state protoimpl.MessageState
@@ -133,7 +178,10 @@
// Dataplane certificate settings
DpCert *CertificateAuthorityBackend_DpCert `protobuf:"bytes,3,opt,name=dpCert,proto3" json:"dpCert,omitempty"`
// Configuration of the backend
- Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+ Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+ // Mode defines the behaviour of inbound listeners with regard to traffic
+ // encryption
+ Mode CertificateAuthorityBackend_Mode `protobuf:"varint,5,opt,name=mode,proto3,enum=dubbo.mesh.v1alpha1.CertificateAuthorityBackend_Mode" json:"mode,omitempty"`
}
func (x *CertificateAuthorityBackend) Reset() {
@@ -189,13 +237,20 @@
return nil
}
-func (x *CertificateAuthorityBackend) GetConf() *structpb.Struct {
+func (x *CertificateAuthorityBackend) GetConf() *_struct.Struct {
if x != nil {
return x.Conf
}
return nil
}
+func (x *CertificateAuthorityBackend) GetMode() CertificateAuthorityBackend_Mode {
+ if x != nil {
+ return x.Mode
+ }
+ return CertificateAuthorityBackend_STRICT
+}
+
// Networking defines the networking configuration of the mesh
type Networking struct {
state protoimpl.MessageState
@@ -315,11 +370,11 @@
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Percentage of traces that will be sent to the backend (range 0.0 - 100.0).
// Empty value defaults to 100.0%
- Sampling *wrapperspb.DoubleValue `protobuf:"bytes,2,opt,name=sampling,proto3" json:"sampling,omitempty"`
+ Sampling *wrappers.DoubleValue `protobuf:"bytes,2,opt,name=sampling,proto3" json:"sampling,omitempty"`
// Type of the backend (Dubbo ships with 'zipkin')
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
// Configuration of the backend
- Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+ Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
}
func (x *TracingBackend) Reset() {
@@ -361,7 +416,7 @@
return ""
}
-func (x *TracingBackend) GetSampling() *wrapperspb.DoubleValue {
+func (x *TracingBackend) GetSampling() *wrappers.DoubleValue {
if x != nil {
return x.Sampling
}
@@ -375,7 +430,7 @@
return ""
}
-func (x *TracingBackend) GetConf() *structpb.Struct {
+func (x *TracingBackend) GetConf() *_struct.Struct {
if x != nil {
return x.Conf
}
@@ -398,7 +453,7 @@
// Determines whether client and server spans will share the same span
// context. Default: true.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/trace/v3/zipkin.proto#config-trace-v3-zipkinconfig
- SharedSpanContext *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=sharedSpanContext,proto3" json:"sharedSpanContext,omitempty"`
+ SharedSpanContext *wrappers.BoolValue `protobuf:"bytes,4,opt,name=sharedSpanContext,proto3" json:"sharedSpanContext,omitempty"`
}
func (x *ZipkinTracingBackendConfig) Reset() {
@@ -454,7 +509,7 @@
return ""
}
-func (x *ZipkinTracingBackendConfig) GetSharedSpanContext() *wrapperspb.BoolValue {
+func (x *ZipkinTracingBackendConfig) GetSharedSpanContext() *wrappers.BoolValue {
if x != nil {
return x.SharedSpanContext
}
@@ -534,7 +589,7 @@
// Type of the backend (Dubbo ships with 'tcp' and 'file')
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
// Configuration of the backend
- Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+ Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
}
func (x *LoggingBackend) Reset() {
@@ -590,7 +645,7 @@
return ""
}
-func (x *LoggingBackend) GetConf() *structpb.Struct {
+func (x *LoggingBackend) GetConf() *_struct.Struct {
if x != nil {
return x.Conf
}
@@ -820,6 +875,8 @@
// Rotation settings
Rotation *CertificateAuthorityBackend_DpCert_Rotation `protobuf:"bytes,1,opt,name=rotation,proto3" json:"rotation,omitempty"`
+ // Timeout on request to CA for DP certificate generation and retrieval
+ RequestTimeout *duration.Duration `protobuf:"bytes,2,opt,name=requestTimeout,proto3" json:"requestTimeout,omitempty"`
}
func (x *CertificateAuthorityBackend_DpCert) Reset() {
@@ -861,6 +918,13 @@
return nil
}
+func (x *CertificateAuthorityBackend_DpCert) GetRequestTimeout() *duration.Duration {
+ if x != nil {
+ return x.RequestTimeout
+ }
+ return nil
+}
+
// Rotation defines rotation settings for Dataplane certificate
type CertificateAuthorityBackend_DpCert_Rotation struct {
state protoimpl.MessageState
@@ -917,7 +981,7 @@
unknownFields protoimpl.UnknownFields
// Control the passthrough cluster
- Passthrough *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=passthrough,proto3" json:"passthrough,omitempty"`
+ Passthrough *wrappers.BoolValue `protobuf:"bytes,1,opt,name=passthrough,proto3" json:"passthrough,omitempty"`
}
func (x *Networking_Outbound) Reset() {
@@ -952,7 +1016,7 @@
return file_api_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{2, 0}
}
-func (x *Networking_Outbound) GetPassthrough() *wrapperspb.BoolValue {
+func (x *Networking_Outbound) GetPassthrough() *wrappers.BoolValue {
if x != nil {
return x.Passthrough
}
@@ -969,6 +1033,8 @@
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x04, 0x0a, 0x04, 0x4d, 0x65,
0x73, 0x68, 0x12, 0x32, 0x0a, 0x04, 0x6d, 0x74, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
@@ -1003,7 +1069,7 @@
0x02, 0x18, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x04, 0x52, 0x02, 0x10, 0x01, 0xaa, 0x8c, 0x89,
0xa6, 0x01, 0x08, 0x3a, 0x06, 0x0a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
0x0a, 0x3a, 0x08, 0x12, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
- 0x02, 0x68, 0x01, 0x22, 0xd8, 0x02, 0x0a, 0x1b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x02, 0x68, 0x01, 0x22, 0x8a, 0x04, 0x0a, 0x1b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x61, 0x63, 0x6b,
0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
@@ -1015,88 +1081,99 @@
0x43, 0x65, 0x72, 0x74, 0x52, 0x06, 0x64, 0x70, 0x43, 0x65, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x04,
0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
- 0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x1a, 0x92, 0x01, 0x0a, 0x06, 0x44, 0x70,
- 0x43, 0x65, 0x72, 0x74, 0x12, 0x5c, 0x0a, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d,
- 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65, 0x72,
- 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
- 0x79, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x2e,
- 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x1a, 0x2a, 0x0a, 0x08, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e,
- 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c,
- 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x12, 0x44, 0x0a,
- 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67,
- 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f,
- 0x75, 0x6e, 0x64, 0x1a, 0x48, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
- 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65,
- 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x22, 0x72, 0x0a,
- 0x07, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61,
- 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
- 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03,
- 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e,
- 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67,
- 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
- 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63,
- 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x61, 0x6d, 0x70,
- 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75,
- 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
- 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x63,
- 0x6f, 0x6e, 0x66, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x54, 0x72,
- 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x31,
- 0x32, 0x38, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74, 0x72, 0x61,
- 0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70,
- 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
- 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11, 0x73, 0x68,
- 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
- 0x65, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e,
- 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12,
- 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e,
- 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
- 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65,
- 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62,
- 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
- 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08,
- 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x67,
- 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
- 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
- 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
- 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f,
- 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
- 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63,
- 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x4c,
- 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70, 0x4c, 0x6f,
- 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x69, 0x0a, 0x07,
- 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
- 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61,
- 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c, 0x6f, 0x63,
+ 0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x12, 0x49, 0x0a, 0x04, 0x6d, 0x6f, 0x64,
+ 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
+ 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04,
+ 0x6d, 0x6f, 0x64, 0x65, 0x1a, 0xd5, 0x01, 0x0a, 0x06, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x12,
+ 0x5c, 0x0a, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x40, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+ 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x61, 0x63, 0x6b,
+ 0x65, 0x6e, 0x64, 0x2e, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a,
+ 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
+ 0x1a, 0x2a, 0x0a, 0x08, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a,
+ 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, 0x04,
+ 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x10, 0x00,
+ 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, 0x10, 0x01,
+ 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x12,
+ 0x44, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+ 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69,
+ 0x6e, 0x67, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x08, 0x6f, 0x75, 0x74,
+ 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x48, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
+ 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
+ 0x75, 0x65, 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x22,
+ 0x72, 0x0a, 0x07, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65,
+ 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73,
+ 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x69,
+ 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65,
+ 0x6e, 0x64, 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42,
+ 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x61,
+ 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
+ 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x73, 0x61, 0x6d, 0x70,
+ 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52,
+ 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e,
+ 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49,
+ 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74,
+ 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a,
+ 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11,
+ 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+ 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
+ 0x6c, 0x75, 0x65, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
+ 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b,
+ 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63,
+ 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75,
+ 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
+ 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f,
+ 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04,
+ 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
+ 0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c,
+ 0x65, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70,
+ 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x69,
+ 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61,
- 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x7a, 0x6f, 0x6e, 0x65, 0x45,
- 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x7a, 0x6f, 0x6e,
- 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75,
- 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62,
- 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70,
- 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62,
- 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c,
+ 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64,
+ 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x7a, 0x6f, 0x6e,
+ 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x7a,
+ 0x6f, 0x6e, 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74,
+ 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64,
+ 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f,
+ 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1111,50 +1188,55 @@
return file_api_mesh_v1alpha1_mesh_proto_rawDescData
}
+var file_api_mesh_v1alpha1_mesh_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_api_mesh_v1alpha1_mesh_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
var file_api_mesh_v1alpha1_mesh_proto_goTypes = []interface{}{
- (*Mesh)(nil), // 0: dubbo.mesh.v1alpha1.Mesh
- (*CertificateAuthorityBackend)(nil), // 1: dubbo.mesh.v1alpha1.CertificateAuthorityBackend
- (*Networking)(nil), // 2: dubbo.mesh.v1alpha1.Networking
- (*Tracing)(nil), // 3: dubbo.mesh.v1alpha1.Tracing
- (*TracingBackend)(nil), // 4: dubbo.mesh.v1alpha1.TracingBackend
- (*ZipkinTracingBackendConfig)(nil), // 5: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig
- (*Logging)(nil), // 6: dubbo.mesh.v1alpha1.Logging
- (*LoggingBackend)(nil), // 7: dubbo.mesh.v1alpha1.LoggingBackend
- (*FileLoggingBackendConfig)(nil), // 8: dubbo.mesh.v1alpha1.FileLoggingBackendConfig
- (*TcpLoggingBackendConfig)(nil), // 9: dubbo.mesh.v1alpha1.TcpLoggingBackendConfig
- (*Routing)(nil), // 10: dubbo.mesh.v1alpha1.Routing
- (*Mesh_Mtls)(nil), // 11: dubbo.mesh.v1alpha1.Mesh.Mtls
- (*CertificateAuthorityBackend_DpCert)(nil), // 12: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
- (*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 13: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
- (*Networking_Outbound)(nil), // 14: dubbo.mesh.v1alpha1.Networking.Outbound
- (*structpb.Struct)(nil), // 15: google.protobuf.Struct
- (*wrapperspb.DoubleValue)(nil), // 16: google.protobuf.DoubleValue
- (*wrapperspb.BoolValue)(nil), // 17: google.protobuf.BoolValue
+ (CertificateAuthorityBackend_Mode)(0), // 0: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.Mode
+ (*Mesh)(nil), // 1: dubbo.mesh.v1alpha1.Mesh
+ (*CertificateAuthorityBackend)(nil), // 2: dubbo.mesh.v1alpha1.CertificateAuthorityBackend
+ (*Networking)(nil), // 3: dubbo.mesh.v1alpha1.Networking
+ (*Tracing)(nil), // 4: dubbo.mesh.v1alpha1.Tracing
+ (*TracingBackend)(nil), // 5: dubbo.mesh.v1alpha1.TracingBackend
+ (*ZipkinTracingBackendConfig)(nil), // 6: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig
+ (*Logging)(nil), // 7: dubbo.mesh.v1alpha1.Logging
+ (*LoggingBackend)(nil), // 8: dubbo.mesh.v1alpha1.LoggingBackend
+ (*FileLoggingBackendConfig)(nil), // 9: dubbo.mesh.v1alpha1.FileLoggingBackendConfig
+ (*TcpLoggingBackendConfig)(nil), // 10: dubbo.mesh.v1alpha1.TcpLoggingBackendConfig
+ (*Routing)(nil), // 11: dubbo.mesh.v1alpha1.Routing
+ (*Mesh_Mtls)(nil), // 12: dubbo.mesh.v1alpha1.Mesh.Mtls
+ (*CertificateAuthorityBackend_DpCert)(nil), // 13: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+ (*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 14: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+ (*Networking_Outbound)(nil), // 15: dubbo.mesh.v1alpha1.Networking.Outbound
+ (*_struct.Struct)(nil), // 16: google.protobuf.Struct
+ (*wrappers.DoubleValue)(nil), // 17: google.protobuf.DoubleValue
+ (*wrappers.BoolValue)(nil), // 18: google.protobuf.BoolValue
+ (*duration.Duration)(nil), // 19: google.protobuf.Duration
}
var file_api_mesh_v1alpha1_mesh_proto_depIdxs = []int32{
- 11, // 0: dubbo.mesh.v1alpha1.Mesh.mtls:type_name -> dubbo.mesh.v1alpha1.Mesh.Mtls
- 3, // 1: dubbo.mesh.v1alpha1.Mesh.tracing:type_name -> dubbo.mesh.v1alpha1.Tracing
- 6, // 2: dubbo.mesh.v1alpha1.Mesh.logging:type_name -> dubbo.mesh.v1alpha1.Logging
- 2, // 3: dubbo.mesh.v1alpha1.Mesh.networking:type_name -> dubbo.mesh.v1alpha1.Networking
- 10, // 4: dubbo.mesh.v1alpha1.Mesh.routing:type_name -> dubbo.mesh.v1alpha1.Routing
- 12, // 5: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
- 15, // 6: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
- 14, // 7: dubbo.mesh.v1alpha1.Networking.outbound:type_name -> dubbo.mesh.v1alpha1.Networking.Outbound
- 4, // 8: dubbo.mesh.v1alpha1.Tracing.backends:type_name -> dubbo.mesh.v1alpha1.TracingBackend
- 16, // 9: dubbo.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
- 15, // 10: dubbo.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
- 17, // 11: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
- 7, // 12: dubbo.mesh.v1alpha1.Logging.backends:type_name -> dubbo.mesh.v1alpha1.LoggingBackend
- 15, // 13: dubbo.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
- 1, // 14: dubbo.mesh.v1alpha1.Mesh.Mtls.backends:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend
- 13, // 15: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
- 17, // 16: dubbo.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
- 17, // [17:17] is the sub-list for method output_type
- 17, // [17:17] is the sub-list for method input_type
- 17, // [17:17] is the sub-list for extension type_name
- 17, // [17:17] is the sub-list for extension extendee
- 0, // [0:17] is the sub-list for field type_name
+ 12, // 0: dubbo.mesh.v1alpha1.Mesh.mtls:type_name -> dubbo.mesh.v1alpha1.Mesh.Mtls
+ 4, // 1: dubbo.mesh.v1alpha1.Mesh.tracing:type_name -> dubbo.mesh.v1alpha1.Tracing
+ 7, // 2: dubbo.mesh.v1alpha1.Mesh.logging:type_name -> dubbo.mesh.v1alpha1.Logging
+ 3, // 3: dubbo.mesh.v1alpha1.Mesh.networking:type_name -> dubbo.mesh.v1alpha1.Networking
+ 11, // 4: dubbo.mesh.v1alpha1.Mesh.routing:type_name -> dubbo.mesh.v1alpha1.Routing
+ 13, // 5: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+ 16, // 6: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
+ 0, // 7: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.mode:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.Mode
+ 15, // 8: dubbo.mesh.v1alpha1.Networking.outbound:type_name -> dubbo.mesh.v1alpha1.Networking.Outbound
+ 5, // 9: dubbo.mesh.v1alpha1.Tracing.backends:type_name -> dubbo.mesh.v1alpha1.TracingBackend
+ 17, // 10: dubbo.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
+ 16, // 11: dubbo.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
+ 18, // 12: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
+ 8, // 13: dubbo.mesh.v1alpha1.Logging.backends:type_name -> dubbo.mesh.v1alpha1.LoggingBackend
+ 16, // 14: dubbo.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
+ 2, // 15: dubbo.mesh.v1alpha1.Mesh.Mtls.backends:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend
+ 14, // 16: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+ 19, // 17: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.requestTimeout:type_name -> google.protobuf.Duration
+ 18, // 18: dubbo.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
+ 19, // [19:19] is the sub-list for method output_type
+ 19, // [19:19] is the sub-list for method input_type
+ 19, // [19:19] is the sub-list for extension type_name
+ 19, // [19:19] is the sub-list for extension extendee
+ 0, // [0:19] is the sub-list for field type_name
}
func init() { file_api_mesh_v1alpha1_mesh_proto_init() }
@@ -1349,13 +1431,14 @@
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_mesh_v1alpha1_mesh_proto_rawDesc,
- NumEnums: 0,
+ NumEnums: 1,
NumMessages: 15,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_mesh_v1alpha1_mesh_proto_goTypes,
DependencyIndexes: file_api_mesh_v1alpha1_mesh_proto_depIdxs,
+ EnumInfos: file_api_mesh_v1alpha1_mesh_proto_enumTypes,
MessageInfos: file_api_mesh_v1alpha1_mesh_proto_msgTypes,
}.Build()
File_api_mesh_v1alpha1_mesh_proto = out.File
diff --git a/api/mesh/v1alpha1/mesh.proto b/api/mesh/v1alpha1/mesh.proto
index d884efc..160d2b7 100644
--- a/api/mesh/v1alpha1/mesh.proto
+++ b/api/mesh/v1alpha1/mesh.proto
@@ -6,6 +6,7 @@
import "google/protobuf/wrappers.proto";
import "google/protobuf/struct.proto";
+import "google/protobuf/duration.proto";
import "api/mesh/options.proto";
// Mesh defines configuration of a single mesh.
@@ -66,6 +67,8 @@
}
// Rotation settings
Rotation rotation = 1;
+ // Timeout on request to CA for DP certificate generation and retrieval
+ google.protobuf.Duration requestTimeout = 2;
}
// Dataplane certificate settings
@@ -73,6 +76,21 @@
// Configuration of the backend
google.protobuf.Struct conf = 4;
+
+ enum Mode {
+ // A STRICT mode implies that the server validates the connection and
+ // accepts only encrypted TLS traffic
+ STRICT = 0;
+ // A PERMISSIVE mode implies that the outbounds encrypt traffic the same way
+ // it happens in strict mode, but inbounds accept both TLS and plaintext
+ // traffic. This allows applications residing in the mesh to accept requests
+ // from outside of the mesh.
+ PERMISSIVE = 1;
+ }
+
+ // Mode defines the behaviour of inbound listeners with regard to traffic
+ // encryption
+ Mode mode = 5;
}
// Networking defines the networking configuration of the mesh
diff --git a/api/mesh/v1alpha1/selector.pb.go b/api/mesh/v1alpha1/selector.pb.go
new file mode 100644
index 0000000..0894456
--- /dev/null
+++ b/api/mesh/v1alpha1/selector.pb.go
@@ -0,0 +1,156 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: api/mesh/v1alpha1/selector.proto
+
+package v1alpha1
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Selector defines structure for selecting tags for given dataplane
+type Selector struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Tags to match, can be used for both source and destinations
+ Match map[string]string `protobuf:"bytes,1,rep,name=match,proto3" json:"match,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *Selector) Reset() {
+ *x = Selector{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_selector_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Selector) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Selector) ProtoMessage() {}
+
+func (x *Selector) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_selector_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Selector.ProtoReflect.Descriptor instead.
+func (*Selector) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_selector_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Selector) GetMatch() map[string]string {
+ if x != nil {
+ return x.Match
+ }
+ return nil
+}
+
+var File_api_mesh_v1alpha1_selector_proto protoreflect.FileDescriptor
+
+var file_api_mesh_v1alpha1_selector_proto_rawDesc = []byte{
+ 0x0a, 0x20, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x12, 0x13, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+ 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x22, 0x84, 0x01, 0x0a, 0x08, 0x53, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+ 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+ 0x6f, 0x72, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d,
+ 0x61, 0x74, 0x63, 0x68, 0x1a, 0x38, 0x0a, 0x0a, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74,
+ 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x36,
+ 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61,
+ 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_api_mesh_v1alpha1_selector_proto_rawDescOnce sync.Once
+ file_api_mesh_v1alpha1_selector_proto_rawDescData = file_api_mesh_v1alpha1_selector_proto_rawDesc
+)
+
+func file_api_mesh_v1alpha1_selector_proto_rawDescGZIP() []byte {
+ file_api_mesh_v1alpha1_selector_proto_rawDescOnce.Do(func() {
+ file_api_mesh_v1alpha1_selector_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_v1alpha1_selector_proto_rawDescData)
+ })
+ return file_api_mesh_v1alpha1_selector_proto_rawDescData
+}
+
+var file_api_mesh_v1alpha1_selector_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_api_mesh_v1alpha1_selector_proto_goTypes = []interface{}{
+ (*Selector)(nil), // 0: dubbo.mesh.v1alpha1.Selector
+ nil, // 1: dubbo.mesh.v1alpha1.Selector.MatchEntry
+}
+var file_api_mesh_v1alpha1_selector_proto_depIdxs = []int32{
+ 1, // 0: dubbo.mesh.v1alpha1.Selector.match:type_name -> dubbo.mesh.v1alpha1.Selector.MatchEntry
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_api_mesh_v1alpha1_selector_proto_init() }
+func file_api_mesh_v1alpha1_selector_proto_init() {
+ if File_api_mesh_v1alpha1_selector_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_api_mesh_v1alpha1_selector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Selector); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_api_mesh_v1alpha1_selector_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_api_mesh_v1alpha1_selector_proto_goTypes,
+ DependencyIndexes: file_api_mesh_v1alpha1_selector_proto_depIdxs,
+ MessageInfos: file_api_mesh_v1alpha1_selector_proto_msgTypes,
+ }.Build()
+ File_api_mesh_v1alpha1_selector_proto = out.File
+ file_api_mesh_v1alpha1_selector_proto_rawDesc = nil
+ file_api_mesh_v1alpha1_selector_proto_goTypes = nil
+ file_api_mesh_v1alpha1_selector_proto_depIdxs = nil
+}
diff --git a/api/mesh/v1alpha1/selector.proto b/api/mesh/v1alpha1/selector.proto
new file mode 100644
index 0000000..d370c14
--- /dev/null
+++ b/api/mesh/v1alpha1/selector.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package dubbo.mesh.v1alpha1;
+
+option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
+
+// Selector defines structure for selecting tags for given dataplane
+message Selector {
+ // Tags to match, can be used for both source and destinations
+ map<string, string> match = 1;
+}
diff --git a/api/mesh/v1alpha1/timeout.pb.go b/api/mesh/v1alpha1/timeout.pb.go
new file mode 100644
index 0000000..09af4f1
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout.pb.go
@@ -0,0 +1,570 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: api/mesh/v1alpha1/timeout.proto
+
+package v1alpha1
+
+import (
+ _ "github.com/apache/dubbo-kubernetes/api/mesh"
+ duration "github.com/golang/protobuf/ptypes/duration"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Timeout struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // List of selectors to match dataplanes that are sources of traffic.
+ Sources []*Selector `protobuf:"bytes,1,rep,name=sources,proto3" json:"sources,omitempty"`
+ // List of selectors to match services that are destinations of traffic.
+ Destinations []*Selector `protobuf:"bytes,2,rep,name=destinations,proto3" json:"destinations,omitempty"`
+ Conf *Timeout_Conf `protobuf:"bytes,3,opt,name=conf,proto3" json:"conf,omitempty"`
+}
+
+func (x *Timeout) Reset() {
+ *x = Timeout{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Timeout) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout) ProtoMessage() {}
+
+func (x *Timeout) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout.ProtoReflect.Descriptor instead.
+func (*Timeout) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Timeout) GetSources() []*Selector {
+ if x != nil {
+ return x.Sources
+ }
+ return nil
+}
+
+func (x *Timeout) GetDestinations() []*Selector {
+ if x != nil {
+ return x.Destinations
+ }
+ return nil
+}
+
+func (x *Timeout) GetConf() *Timeout_Conf {
+ if x != nil {
+ return x.Conf
+ }
+ return nil
+}
+
+type Timeout_Conf struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // ConnectTimeout defines time to establish connection
+ ConnectTimeout *duration.Duration `protobuf:"bytes,1,opt,name=connect_timeout,json=connectTimeout,proto3" json:"connect_timeout,omitempty"`
+ Tcp *Timeout_Conf_Tcp `protobuf:"bytes,2,opt,name=tcp,proto3" json:"tcp,omitempty"`
+ Http *Timeout_Conf_Http `protobuf:"bytes,3,opt,name=http,proto3" json:"http,omitempty"`
+ // Deprecated: set parameters through Http section
+ Grpc *Timeout_Conf_Grpc `protobuf:"bytes,4,opt,name=grpc,proto3" json:"grpc,omitempty"`
+}
+
+func (x *Timeout_Conf) Reset() {
+ *x = Timeout_Conf{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Timeout_Conf) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf) ProtoMessage() {}
+
+func (x *Timeout_Conf) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *Timeout_Conf) GetConnectTimeout() *duration.Duration {
+ if x != nil {
+ return x.ConnectTimeout
+ }
+ return nil
+}
+
+func (x *Timeout_Conf) GetTcp() *Timeout_Conf_Tcp {
+ if x != nil {
+ return x.Tcp
+ }
+ return nil
+}
+
+func (x *Timeout_Conf) GetHttp() *Timeout_Conf_Http {
+ if x != nil {
+ return x.Http
+ }
+ return nil
+}
+
+func (x *Timeout_Conf) GetGrpc() *Timeout_Conf_Grpc {
+ if x != nil {
+ return x.Grpc
+ }
+ return nil
+}
+
+// Tcp defines timeouts that are applied when the protocol is TCP
+type Timeout_Conf_Tcp struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // IdleTimeout is defined as the period in which there are no bytes sent
+ // or received on either the upstream or downstream connection
+ IdleTimeout *duration.Duration `protobuf:"bytes,1,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
+}
+
+func (x *Timeout_Conf_Tcp) Reset() {
+ *x = Timeout_Conf_Tcp{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Timeout_Conf_Tcp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Tcp) ProtoMessage() {}
+
+func (x *Timeout_Conf_Tcp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Tcp.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Tcp) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 0}
+}
+
+func (x *Timeout_Conf_Tcp) GetIdleTimeout() *duration.Duration {
+ if x != nil {
+ return x.IdleTimeout
+ }
+ return nil
+}
+
+// Http defines timeouts that are applied when the protocol is HTTP
+type Timeout_Conf_Http struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // RequestTimeout is a span between the point at which the entire
+ // downstream request (i.e. end-of-stream) has been processed and when the
+ // upstream response has been completely processed
+ RequestTimeout *duration.Duration `protobuf:"bytes,1,opt,name=request_timeout,json=requestTimeout,proto3" json:"request_timeout,omitempty"`
+ // IdleTimeout is the time at which a downstream or upstream connection
+ // will be terminated if there are no active streams
+ IdleTimeout *duration.Duration `protobuf:"bytes,2,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
+ // StreamIdleTimeout is the amount of time that the connection manager
+ // will allow a stream to exist with no upstream or downstream activity
+ StreamIdleTimeout *duration.Duration `protobuf:"bytes,3,opt,name=stream_idle_timeout,json=streamIdleTimeout,proto3" json:"stream_idle_timeout,omitempty"`
+ // MaxStreamDuration is the maximum time that a stream’s lifetime will
+ // span
+ MaxStreamDuration *duration.Duration `protobuf:"bytes,4,opt,name=max_stream_duration,json=maxStreamDuration,proto3" json:"max_stream_duration,omitempty"`
+}
+
+func (x *Timeout_Conf_Http) Reset() {
+ *x = Timeout_Conf_Http{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Timeout_Conf_Http) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Http) ProtoMessage() {}
+
+func (x *Timeout_Conf_Http) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Http.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Http) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 1}
+}
+
+func (x *Timeout_Conf_Http) GetRequestTimeout() *duration.Duration {
+ if x != nil {
+ return x.RequestTimeout
+ }
+ return nil
+}
+
+func (x *Timeout_Conf_Http) GetIdleTimeout() *duration.Duration {
+ if x != nil {
+ return x.IdleTimeout
+ }
+ return nil
+}
+
+func (x *Timeout_Conf_Http) GetStreamIdleTimeout() *duration.Duration {
+ if x != nil {
+ return x.StreamIdleTimeout
+ }
+ return nil
+}
+
+func (x *Timeout_Conf_Http) GetMaxStreamDuration() *duration.Duration {
+ if x != nil {
+ return x.MaxStreamDuration
+ }
+ return nil
+}
+
+// Grpc defines timeouts that are applied when the protocol is GRPC
+type Timeout_Conf_Grpc struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // StreamIdleTimeout is the amount of time that the connection manager
+ // will allow a stream to exist with no upstream or downstream activity
+ // Deprecated: use Http.StreamIdleTimeout instead
+ StreamIdleTimeout *duration.Duration `protobuf:"bytes,1,opt,name=stream_idle_timeout,json=streamIdleTimeout,proto3" json:"stream_idle_timeout,omitempty"`
+ // MaxStreamDuration is the maximum time that a stream’s lifetime will
+ // span
+ // Deprecated: use Http.MaxStreamDuration instead
+ MaxStreamDuration *duration.Duration `protobuf:"bytes,2,opt,name=max_stream_duration,json=maxStreamDuration,proto3" json:"max_stream_duration,omitempty"`
+}
+
+func (x *Timeout_Conf_Grpc) Reset() {
+ *x = Timeout_Conf_Grpc{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Timeout_Conf_Grpc) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Grpc) ProtoMessage() {}
+
+func (x *Timeout_Conf_Grpc) ProtoReflect() protoreflect.Message {
+ mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Grpc.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Grpc) Descriptor() ([]byte, []int) {
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 2}
+}
+
+func (x *Timeout_Conf_Grpc) GetStreamIdleTimeout() *duration.Duration {
+ if x != nil {
+ return x.StreamIdleTimeout
+ }
+ return nil
+}
+
+func (x *Timeout_Conf_Grpc) GetMaxStreamDuration() *duration.Duration {
+ if x != nil {
+ return x.MaxStreamDuration
+ }
+ return nil
+}
+
+var File_api_mesh_v1alpha1_timeout_proto protoreflect.FileDescriptor
+
+var file_api_mesh_v1alpha1_timeout_proto_rawDesc = []byte{
+ 0x0a, 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x13, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68,
+ 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20,
+ 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x22, 0x96, 0x08, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x37, 0x0a, 0x07,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
+ 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x75,
+ 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d,
+ 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d,
+ 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x1a,
+ 0x80, 0x06, 0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x42, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f,
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x37, 0x0a, 0x03,
+ 0x74, 0x63, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x64, 0x75, 0x62, 0x62,
+ 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+ 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x54, 0x63, 0x70,
+ 0x52, 0x03, 0x74, 0x63, 0x70, 0x12, 0x3a, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+ 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+ 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74,
+ 0x70, 0x12, 0x3a, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
+ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f,
+ 0x6e, 0x66, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x52, 0x04, 0x67, 0x72, 0x70, 0x63, 0x1a, 0x43, 0x0a,
+ 0x03, 0x54, 0x63, 0x70, 0x12, 0x3c, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+ 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f,
+ 0x75, 0x74, 0x1a, 0x9e, 0x02, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x42, 0x0a, 0x0f, 0x72,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12,
+ 0x3c, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a,
+ 0x13, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+ 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x6c,
+ 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f,
+ 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x11, 0x6d, 0x61, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x1a, 0x9c, 0x01, 0x0a, 0x04, 0x47, 0x72, 0x70, 0x63, 0x12, 0x49, 0x0a, 0x13,
+ 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+ 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x6c, 0x65,
+ 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x73,
+ 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x11, 0x6d, 0x61, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x3a, 0x55, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x11, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65,
+ 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
+ 0x09, 0x12, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x06,
+ 0x22, 0x04, 0x6d, 0x65, 0x73, 0x68, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x04, 0x52, 0x02, 0x10, 0x01,
+ 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0b, 0x3a, 0x09, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+ 0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x68, 0x01, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74,
+ 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64,
+ 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f,
+ 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_api_mesh_v1alpha1_timeout_proto_rawDescOnce sync.Once
+ file_api_mesh_v1alpha1_timeout_proto_rawDescData = file_api_mesh_v1alpha1_timeout_proto_rawDesc
+)
+
+func file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP() []byte {
+ file_api_mesh_v1alpha1_timeout_proto_rawDescOnce.Do(func() {
+ file_api_mesh_v1alpha1_timeout_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_v1alpha1_timeout_proto_rawDescData)
+ })
+ return file_api_mesh_v1alpha1_timeout_proto_rawDescData
+}
+
+var file_api_mesh_v1alpha1_timeout_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_api_mesh_v1alpha1_timeout_proto_goTypes = []interface{}{
+ (*Timeout)(nil), // 0: dubbo.mesh.v1alpha1.Timeout
+ (*Timeout_Conf)(nil), // 1: dubbo.mesh.v1alpha1.Timeout.Conf
+ (*Timeout_Conf_Tcp)(nil), // 2: dubbo.mesh.v1alpha1.Timeout.Conf.Tcp
+ (*Timeout_Conf_Http)(nil), // 3: dubbo.mesh.v1alpha1.Timeout.Conf.Http
+ (*Timeout_Conf_Grpc)(nil), // 4: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc
+ (*Selector)(nil), // 5: dubbo.mesh.v1alpha1.Selector
+ (*duration.Duration)(nil), // 6: google.protobuf.Duration
+}
+var file_api_mesh_v1alpha1_timeout_proto_depIdxs = []int32{
+ 5, // 0: dubbo.mesh.v1alpha1.Timeout.sources:type_name -> dubbo.mesh.v1alpha1.Selector
+ 5, // 1: dubbo.mesh.v1alpha1.Timeout.destinations:type_name -> dubbo.mesh.v1alpha1.Selector
+ 1, // 2: dubbo.mesh.v1alpha1.Timeout.conf:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf
+ 6, // 3: dubbo.mesh.v1alpha1.Timeout.Conf.connect_timeout:type_name -> google.protobuf.Duration
+ 2, // 4: dubbo.mesh.v1alpha1.Timeout.Conf.tcp:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Tcp
+ 3, // 5: dubbo.mesh.v1alpha1.Timeout.Conf.http:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Http
+ 4, // 6: dubbo.mesh.v1alpha1.Timeout.Conf.grpc:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Grpc
+ 6, // 7: dubbo.mesh.v1alpha1.Timeout.Conf.Tcp.idle_timeout:type_name -> google.protobuf.Duration
+ 6, // 8: dubbo.mesh.v1alpha1.Timeout.Conf.Http.request_timeout:type_name -> google.protobuf.Duration
+ 6, // 9: dubbo.mesh.v1alpha1.Timeout.Conf.Http.idle_timeout:type_name -> google.protobuf.Duration
+ 6, // 10: dubbo.mesh.v1alpha1.Timeout.Conf.Http.stream_idle_timeout:type_name -> google.protobuf.Duration
+ 6, // 11: dubbo.mesh.v1alpha1.Timeout.Conf.Http.max_stream_duration:type_name -> google.protobuf.Duration
+ 6, // 12: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc.stream_idle_timeout:type_name -> google.protobuf.Duration
+ 6, // 13: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc.max_stream_duration:type_name -> google.protobuf.Duration
+ 14, // [14:14] is the sub-list for method output_type
+ 14, // [14:14] is the sub-list for method input_type
+ 14, // [14:14] is the sub-list for extension type_name
+ 14, // [14:14] is the sub-list for extension extendee
+ 0, // [0:14] is the sub-list for field type_name
+}
+
+func init() { file_api_mesh_v1alpha1_timeout_proto_init() }
+func file_api_mesh_v1alpha1_timeout_proto_init() {
+ if File_api_mesh_v1alpha1_timeout_proto != nil {
+ return
+ }
+ file_api_mesh_v1alpha1_selector_proto_init()
+ if !protoimpl.UnsafeEnabled {
+ file_api_mesh_v1alpha1_timeout_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Timeout); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_api_mesh_v1alpha1_timeout_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Timeout_Conf); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_api_mesh_v1alpha1_timeout_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Timeout_Conf_Tcp); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_api_mesh_v1alpha1_timeout_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Timeout_Conf_Http); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_api_mesh_v1alpha1_timeout_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Timeout_Conf_Grpc); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_api_mesh_v1alpha1_timeout_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 5,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_api_mesh_v1alpha1_timeout_proto_goTypes,
+ DependencyIndexes: file_api_mesh_v1alpha1_timeout_proto_depIdxs,
+ MessageInfos: file_api_mesh_v1alpha1_timeout_proto_msgTypes,
+ }.Build()
+ File_api_mesh_v1alpha1_timeout_proto = out.File
+ file_api_mesh_v1alpha1_timeout_proto_rawDesc = nil
+ file_api_mesh_v1alpha1_timeout_proto_goTypes = nil
+ file_api_mesh_v1alpha1_timeout_proto_depIdxs = nil
+}
diff --git a/api/mesh/v1alpha1/timeout.proto b/api/mesh/v1alpha1/timeout.proto
new file mode 100644
index 0000000..82ce555
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout.proto
@@ -0,0 +1,71 @@
+syntax = "proto3";
+
+package dubbo.mesh.v1alpha1;
+
+option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
+
+import "api/mesh/options.proto";
+import "api/mesh/v1alpha1/selector.proto";
+import "google/protobuf/duration.proto";
+
+message Timeout {
+
+ option (dubbo.mesh.resource).name = "TimeoutResource";
+ option (dubbo.mesh.resource).type = "Timeout";
+ option (dubbo.mesh.resource).package = "mesh";
+ option (dubbo.mesh.resource).dds.send_to_zone = true;
+ option (dubbo.mesh.resource).ws.name = "timeout";
+ option (dubbo.mesh.resource).allow_to_inspect = true;
+
+ // List of selectors to match dataplanes that are sources of traffic.
+ repeated Selector sources = 1;
+
+ // List of selectors to match services that are destinations of traffic.
+ repeated Selector destinations = 2;
+
+ message Conf {
+ // ConnectTimeout defines time to establish connection
+ google.protobuf.Duration connect_timeout = 1;
+
+ // Tcp defines timeouts that are applied when the protocol is TCP
+ message Tcp {
+ // IdleTimeout is defined as the period in which there are no bytes sent
+ // or received on either the upstream or downstream connection
+ google.protobuf.Duration idle_timeout = 1;
+ }
+ Tcp tcp = 2;
+
+ // Http defines timeouts that are applied when the protocol is HTTP
+ message Http {
+ // RequestTimeout is a span between the point at which the entire
+ // downstream request (i.e. end-of-stream) has been processed and when the
+ // upstream response has been completely processed
+ google.protobuf.Duration request_timeout = 1;
+ // IdleTimeout is the time at which a downstream or upstream connection
+ // will be terminated if there are no active streams
+ google.protobuf.Duration idle_timeout = 2;
+ // StreamIdleTimeout is the amount of time that the connection manager
+ // will allow a stream to exist with no upstream or downstream activity
+ google.protobuf.Duration stream_idle_timeout = 3;
+ // MaxStreamDuration is the maximum time that a stream’s lifetime will
+ // span
+ google.protobuf.Duration max_stream_duration = 4;
+ }
+ Http http = 3;
+
+ // Grpc defines timeouts that are applied when the protocol is GRPC
+ message Grpc {
+ // StreamIdleTimeout is the amount of time that the connection manager
+ // will allow a stream to exist with no upstream or downstream activity
+ // Deprecated: use Http.StreamIdleTimeout instead
+ google.protobuf.Duration stream_idle_timeout = 1;
+ // MaxStreamDuration is the maximum time that a stream’s lifetime will
+ // span
+ // Deprecated: use Http.MaxStreamDuration instead
+ google.protobuf.Duration max_stream_duration = 2;
+ }
+ // Deprecated: set parameters through Http section
+ Grpc grpc = 4;
+ }
+ Conf conf = 3;
+}
diff --git a/api/mesh/v1alpha1/timeout_helpers.go b/api/mesh/v1alpha1/timeout_helpers.go
new file mode 100644
index 0000000..d9b377e
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout_helpers.go
@@ -0,0 +1,16 @@
+package v1alpha1
+
+import (
+ "time"
+)
+
+func (x *Timeout_Conf) GetConnectTimeoutOrDefault(defaultConnectTimeout time.Duration) time.Duration {
+ if x == nil {
+ return defaultConnectTimeout
+ }
+ connectTimeout := x.GetConnectTimeout()
+ if connectTimeout == nil {
+ return defaultConnectTimeout
+ }
+ return connectTimeout.AsDuration()
+}
diff --git a/api/mesh/v1alpha1/zoneegress_helpers.go b/api/mesh/v1alpha1/zoneegress_helpers.go
new file mode 100644
index 0000000..2adb036
--- /dev/null
+++ b/api/mesh/v1alpha1/zoneegress_helpers.go
@@ -0,0 +1,3 @@
+package v1alpha1
+
+const ZoneEgressServiceName = "zone-egress"
diff --git a/api/system/v1alpha1/datasource.pb.go b/api/system/v1alpha1/datasource.pb.go
index dbd31c5..6e04824 100644
--- a/api/system/v1alpha1/datasource.pb.go
+++ b/api/system/v1alpha1/datasource.pb.go
@@ -1,28 +1,20 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.28.1
-// protoc v3.20.0
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
// source: api/system/v1alpha1/datasource.proto
package v1alpha1
import (
+ _ "github.com/apache/dubbo-kubernetes/api/mesh"
+ wrappers "github.com/golang/protobuf/ptypes/wrappers"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
- wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
-)
-
-import (
- _ "github.com/apache/dubbo-kubernetes/api/mesh"
-)
-
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -37,7 +29,6 @@
unknownFields protoimpl.UnknownFields
// Types that are assignable to Type:
- //
// *DataSource_Secret
// *DataSource_File
// *DataSource_Inline
@@ -98,7 +89,7 @@
return ""
}
-func (x *DataSource) GetInline() *wrapperspb.BytesValue {
+func (x *DataSource) GetInline() *wrappers.BytesValue {
if x, ok := x.GetType().(*DataSource_Inline); ok {
return x.Inline
}
@@ -129,7 +120,7 @@
type DataSource_Inline struct {
// Data source is inline bytes.
- Inline *wrapperspb.BytesValue `protobuf:"bytes,3,opt,name=inline,proto3,oneof"`
+ Inline *wrappers.BytesValue `protobuf:"bytes,3,opt,name=inline,proto3,oneof"`
}
type DataSource_InlineString struct {
@@ -192,8 +183,8 @@
var file_api_system_v1alpha1_datasource_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_api_system_v1alpha1_datasource_proto_goTypes = []interface{}{
- (*DataSource)(nil), // 0: dubbo.system.v1alpha1.DataSource
- (*wrapperspb.BytesValue)(nil), // 1: google.protobuf.BytesValue
+ (*DataSource)(nil), // 0: dubbo.system.v1alpha1.DataSource
+ (*wrappers.BytesValue)(nil), // 1: google.protobuf.BytesValue
}
var file_api_system_v1alpha1_datasource_proto_depIdxs = []int32{
1, // 0: dubbo.system.v1alpha1.DataSource.inline:type_name -> google.protobuf.BytesValue
diff --git a/app/dubbo-ui/fs.go b/app/dubbo-ui/fs.go
index a53af4a..c80526f 100644
--- a/app/dubbo-ui/fs.go
+++ b/app/dubbo-ui/fs.go
@@ -28,10 +28,28 @@
//go:embed dist/*
var Data embed.FS
+type tryOrDefault struct {
+ fileSystem fs.FS
+ fallbackFile string
+}
+
+func (t tryOrDefault) Open(name string) (fs.File, error) {
+ f, err := t.fileSystem.Open(name)
+ if err == nil {
+ return f, nil
+ }
+ return t.fileSystem.Open(t.fallbackFile)
+}
+
var FS = func() fs.FS {
fsys, err := fs.Sub(Data, "dist/admin")
if err != nil {
panic(err)
}
+ // Adapt Vue's HTML5 history mode
+ fsys = tryOrDefault{
+ fileSystem: fsys,
+ fallbackFile: "index.html",
+ }
return fsys
}
diff --git a/dubboctl/cmd/manifest.go b/dubboctl/cmd/manifest.go
index fa4acec..c552300 100644
--- a/dubboctl/cmd/manifest.go
+++ b/dubboctl/cmd/manifest.go
@@ -16,18 +16,19 @@
package cmd
import (
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/manifest"
"github.com/spf13/cobra"
)
-func addManifest(rootCmd *cobra.Command) {
+func AddManifest(rootCmd *cobra.Command) {
manifestCmd := &cobra.Command{
Use: "manifest",
Short: "Commands related to manifest",
Long: "Commands help user to generate manifest and install manifest",
}
- ConfigManifestGenerateCmd(manifestCmd)
- ConfigManifestInstallCmd(manifestCmd)
- ConfigManifestUninstallCmd(manifestCmd)
- ConfigManifestDiffCmd(manifestCmd)
+ manifest.ConfigManifestGenerateCmd(manifestCmd)
+ manifest.ConfigManifestInstallCmd(manifestCmd)
+ manifest.ConfigManifestUninstallCmd(manifestCmd)
+ manifest.ConfigManifestDiffCmd(manifestCmd)
rootCmd.AddCommand(manifestCmd)
}
diff --git a/dubboctl/cmd/manifest_test.go b/dubboctl/cmd/manifest_test.go
index 4435414..29fe869 100644
--- a/dubboctl/cmd/manifest_test.go
+++ b/dubboctl/cmd/manifest_test.go
@@ -18,13 +18,16 @@
import (
"bytes"
"os"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
"strings"
"testing"
)
-import (
- "sigs.k8s.io/controller-runtime/pkg/client/fake"
-)
+//var (
+// // TestInstallFlag and TestCli are uses for black box testing
+// TestInstallFlag bool
+// TestCli client.Client
+//)
func TestManifestGenerate(t *testing.T) {
tests := []struct {
@@ -74,7 +77,7 @@
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
- testExecute(t, test.cmd, test.wantErr)
+ testManifestExecute(t, test.cmd, test.wantErr)
// remove temporary dir
if test.temp != "" {
os.RemoveAll(test.temp)
@@ -89,10 +92,10 @@
cmd string
wantErr bool
}{
- {
- desc: "without any flag",
- cmd: "manifest install",
- },
+ //{
+ // desc: "without any flag",
+ // cmd: "manifest install",
+ //},
}
// For now, we do not use envTest to do black box testing
TestInstallFlag = true
@@ -100,7 +103,7 @@
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
- testExecute(t, test.cmd, test.wantErr)
+ testManifestExecute(t, test.cmd, test.wantErr)
})
}
}
@@ -113,11 +116,11 @@
cmd string
wantErr bool
}{
- {
- desc: "without any flag",
- before: "manifest install",
- cmd: "manifest uninstall",
- },
+ //{
+ // desc: "without any flag",
+ // before: "manifest install",
+ // cmd: "manifest uninstall",
+ //},
}
// For now, we do not use envTest to do black box testing
TestInstallFlag = true
@@ -126,8 +129,8 @@
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
// prepare existing resources
- testExecute(t, test.before, false)
- testExecute(t, test.cmd, test.wantErr)
+ testManifestExecute(t, test.before, false)
+ testManifestExecute(t, test.cmd, test.wantErr)
})
}
}
@@ -156,9 +159,9 @@
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
for _, before := range test.befores {
- testExecute(t, before, false)
+ testManifestExecute(t, before, false)
}
- testExecute(t, test.cmd, test.wantErr)
+ testManifestExecute(t, test.cmd, test.wantErr)
for _, temp := range test.temps {
if temp != "" {
os.RemoveAll(temp)
@@ -168,16 +171,16 @@
}
}
-func testExecute(t *testing.T, cmd string, wantErr bool) string {
+func testManifestExecute(t *testing.T, cmds string, wantErr bool) string {
var out bytes.Buffer
- args := strings.Split(cmd, " ")
- rootCmd := getRootCmd(args)
+ args := strings.Split(cmds, " ")
+ rootCmd := GetRootCmd(args)
rootCmd.SetOut(&out)
if err := rootCmd.Execute(); err != nil {
if wantErr {
return ""
}
- t.Errorf("execute %s failed, err: %s", cmd, err)
+ t.Errorf("execute %s failed, err: %s", cmds, err)
return ""
}
if wantErr {
diff --git a/dubboctl/cmd/profile.go b/dubboctl/cmd/profile.go
index a59cf2e..3f4d2ce 100644
--- a/dubboctl/cmd/profile.go
+++ b/dubboctl/cmd/profile.go
@@ -16,6 +16,7 @@
package cmd
import (
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/profile"
"github.com/spf13/cobra"
)
@@ -25,8 +26,8 @@
Short: "Commands related to profiles",
Long: "Commands help user to list and describe profiles",
}
- ConfigProfileListCmd(profileCmd)
- ConfigProfileDiffCmd(profileCmd)
+ profile.ConfigProfileListCmd(profileCmd)
+ profile.ConfigProfileDiffCmd(profileCmd)
rootCmd.AddCommand(profileCmd)
}
diff --git a/dubboctl/cmd/profile_test.go b/dubboctl/cmd/profile_test.go
index 3c0dd4d..00057d1 100644
--- a/dubboctl/cmd/profile_test.go
+++ b/dubboctl/cmd/profile_test.go
@@ -16,6 +16,8 @@
package cmd
import (
+ "bytes"
+ "strings"
"testing"
)
@@ -67,7 +69,7 @@
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
- res := testExecute(t, test.cmd, test.wantErr)
+ res := testProfileExecute(t, test.cmd, test.wantErr)
if test.want != "" && test.want != res {
t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want, res)
return
@@ -120,7 +122,7 @@
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
- res := testExecute(t, test.cmd, test.wantErr)
+ res := testProfileExecute(t, test.cmd, test.wantErr)
if test.want != "" && test.want != res {
t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want, res)
return
@@ -128,3 +130,22 @@
})
}
}
+
+func testProfileExecute(t *testing.T, cmds string, wantErr bool) string {
+ var out bytes.Buffer
+ args := strings.Split(cmds, " ")
+ rootCmd := GetRootCmd(args)
+ rootCmd.SetOut(&out)
+ if err := rootCmd.Execute(); err != nil {
+ if wantErr {
+ return ""
+ }
+ t.Errorf("execute %s failed, err: %s", cmds, err)
+ return ""
+ }
+ if wantErr {
+ t.Errorf("want err but got no err")
+ return ""
+ }
+ return out.String()
+}
diff --git a/dubboctl/cmd/repository_test.go b/dubboctl/cmd/repository_test.go
deleted file mode 100644
index 169af73..0000000
--- a/dubboctl/cmd/repository_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cmd
-
-import (
- "testing"
-)
-
-// TestRepository_List ensures that the 'list' subcommand shows the client's
-// set of repositories by name for builtin repositories, by explicitly
-// setting the repositories' path to a new path which includes no others.
-func TestRepository_List(t *testing.T) {
- _ = fromTempDirectory(t)
-
- cmd := NewRepositoryListCmd(NewClient)
- cmd.SetArgs([]string{}) // Do not use test command args
-
- // Execute the command, capturing the output sent to stdout
- stdout := piped(t)
- if err := cmd.Execute(); err != nil {
- t.Fatal(err)
- }
-
- // Assert the output matches expect (whitespace trimmed)
- expect := "default"
- output := stdout()
- if output != expect {
- t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output)
- }
-}
diff --git a/dubboctl/cmd/root.go b/dubboctl/cmd/root.go
index 726a4d8..751c302 100644
--- a/dubboctl/cmd/root.go
+++ b/dubboctl/cmd/root.go
@@ -16,10 +16,11 @@
package cmd
import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/dashboard"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/generate"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/proxy"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/registry"
)
import (
@@ -29,21 +30,18 @@
)
import (
- "github.com/apache/dubbo-kubernetes/pkg/core"
cmd2 "github.com/apache/dubbo-kubernetes/pkg/core/cmd"
)
type RootCommandConfig struct {
Name string
- NewClient ClientFactory
+ NewClient deploy.ClientFactory
}
-var controlPlaneLog = core.Log.WithName("dubboctl")
-
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(args []string) {
- rootCmd := getRootCmd(args)
+ rootCmd := GetRootCmd(args)
// when flag error occurs, print usage string.
// but if an error occurs when executing command, usage string will not be printed.
rootCmd.SetFlagErrorFunc(func(command *cobra.Command, err error) error {
@@ -55,7 +53,7 @@
cobra.CheckErr(rootCmd.Execute())
}
-func getRootCmd(args []string) *cobra.Command {
+func GetRootCmd(args []string) *cobra.Command {
// rootCmd represents the base command when called without any subcommands
rootCmd := &cobra.Command{
Use: "dubboctl",
@@ -76,7 +74,7 @@
viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
newClient := cfg.NewClient
if newClient == nil {
- newClient = NewClient
+ newClient = deploy.NewClient
}
addSubCommands(rootCmd, newClient)
@@ -84,132 +82,15 @@
return rootCmd
}
-func addSubCommands(rootCmd *cobra.Command, newClient ClientFactory) {
- addBuild(rootCmd, newClient)
- addCreate(rootCmd, newClient)
- addRepository(rootCmd, newClient)
- addDeploy(rootCmd, newClient)
- addManifest(rootCmd)
- addGenerate(rootCmd)
+func addSubCommands(rootCmd *cobra.Command, newClient deploy.ClientFactory) {
+ deploy.AddBuild(rootCmd, newClient)
+ deploy.AddCreate(rootCmd, newClient)
+ deploy.AddRepository(rootCmd, newClient)
+ deploy.AddDeploy(rootCmd, newClient)
+ AddManifest(rootCmd)
+ generate.AddGenerate(rootCmd)
addProfile(rootCmd)
- addDashboard(rootCmd)
- addRegistryCmd(rootCmd)
- addProxy(cmd2.DefaultRunCmdOpts, rootCmd)
-}
-
-// bindFunc which conforms to the cobra PreRunE method signature
-type bindFunc func(*cobra.Command, []string) error
-
-// bindEnv returns a bindFunc that binds env vars to the named flags.
-func bindEnv(flags ...string) bindFunc {
- return func(cmd *cobra.Command, args []string) (err error) {
- for _, flag := range flags {
- if err = viper.BindPFlag(flag, cmd.Flags().Lookup(flag)); err != nil {
- return
- }
- }
- viper.AutomaticEnv() // read in environment variables for DUBBO_<flag>
- viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
- return
- }
-}
-
-// addConfirmFlag ensures common text/wording when the --path flag is used
-func addConfirmFlag(cmd *cobra.Command, dflt bool) {
- cmd.Flags().BoolP("confirm", "c", dflt, "Prompt to confirm options interactively ($DUBBO_CONFIRM)")
-}
-
-// addPathFlag ensures common text/wording when the --path flag is used
-func addPathFlag(cmd *cobra.Command) {
- cmd.Flags().StringP("path", "p", "", "Path to the application. Default is current directory ($DUBBO_PATH)")
-}
-
-// surveySelectDefault returns 'value' if defined and exists in 'options'.
-// Otherwise, options[0] is returned if it exists. Empty string otherwise.
-//
-// Usage Example:
-//
-// languages := []string{ "go", "node", "rust" },
-// survey.Select{
-// Options: options,
-// Default: surveySelectDefaut(cfg.Language, languages),
-// }
-//
-// Summary:
-//
-// This protects against an incorrectly initialized survey.Select when the user
-// has provided a nonexistant option (validation is handled elsewhere) or
-// when a value is required but there exists no defaults (no default value on
-// the associated flag).
-//
-// Explanation:
-//
-// The above example chooses the default for the Survey (--confirm) question
-// in a way that works with user-provided flag and environment variable values.
-//
-// `cfg.Language` is the current value set in the config struct, which is
-// populated from (in ascending order of precedence):
-// static flag default, associated environment variable, or command flag.
-// `languages` are the options which are being used by the survey select.
-//
-// This cascade allows for the Survey questions to be properly pre-initialzed
-// with their associated environment variables or flags. For example,
-// A user whose default language is set to 'node' using the global environment
-// variable FUNC_LANGUAGE will have that option pre-selected when running
-// `dubbo create -c`.
-//
-// The 'survey' package expects the value of the Default member to exist
-// in the 'Options' member. This is not possible when user-provided data is
-// allowed for the default, hence this logic is necessary.
-//
-// For example, when the user is using prompts (--confirm) to select from a set
-// of options, but the associated flag either has an unrecognized value, or no
-// value at all, without this logic the resulting select prompt would be
-// initialized with this as the default value, and the act of what appears to
-// be choose the first option displayed does not overwrite the invalid default.
-// It could perhaps be argued this is a shortcoming in the survey package, but
-// it is also clearly an error to provide invalid data for a default.
-func surveySelectDefault(value string, options []string) string {
- for _, v := range options {
- if value == v {
- return v // The provided value is acceptable
- }
- }
- if len(options) > 0 {
- return options[0] // Sync with the option which will be shown by the UX
- }
- // Either the value is not an option or there are no options. Either of
- // which should fail proper validation
- return ""
-}
-
-// cwd returns the current working directory or exits 1 printing the error.
-func cwd() (cwd string) {
- cwd, err := os.Getwd()
- if err != nil {
- panic(fmt.Sprintf("Unable to determine current working directory: %v", err))
- }
- return cwd
-}
-
-// deriveNameAndAbsolutePathFromPath returns application name and absolute path
-// to the application project root. The input parameter path could be one of:
-// 'relative/path/to/foo', '/absolute/path/to/foo', 'foo' or ”.
-func deriveNameAndAbsolutePathFromPath(path string) (string, string) {
- var absPath string
-
- // If path is not specified, we would like to use current working dir
- if path == "" {
- path = cwd()
- }
-
- // Expand the passed function name to its absolute path
- absPath, err := filepath.Abs(path)
- if err != nil {
- return "", ""
- }
-
- // Get the name of the function, which equals to name of the current directory
- pathParts := strings.Split(strings.TrimRight(path, string(os.PathSeparator)), string(os.PathSeparator))
- return pathParts[len(pathParts)-1], absPath
+ dashboard.AddDashboard(rootCmd)
+ registry.AddRegistryCmd(rootCmd)
+ proxy.AddProxy(cmd2.DefaultRunCmdOpts, rootCmd)
}
diff --git a/dubboctl/pkg/common/common.go b/dubboctl/pkg/common/common.go
new file mode 100644
index 0000000..bc8b8e2
--- /dev/null
+++ b/dubboctl/pkg/common/common.go
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import (
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/ory/viper"
+ "github.com/spf13/cobra"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var ControlPlaneLog = core.Log.WithName("dubboctl")
+
+// bindFunc which conforms to the cobra PreRunE method signature
+type bindFunc func(*cobra.Command, []string) error
+
+// BindEnv returns a bindFunc that binds env vars to the named flags.
+func BindEnv(flags ...string) bindFunc {
+ return func(cmd *cobra.Command, args []string) (err error) {
+ for _, flag := range flags {
+ if err = viper.BindPFlag(flag, cmd.Flags().Lookup(flag)); err != nil {
+ return
+ }
+ }
+ viper.AutomaticEnv() // read in environment variables for DUBBO_<flag>
+ viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
+ return
+ }
+}
+
+func WriteFile(filename string, data []byte, perm os.FileMode) error {
+ if err := os.MkdirAll(filepath.Dir(filename), perm); err != nil {
+ return err
+ }
+ return os.WriteFile(filename, data, perm)
+}
+
+// AddConfirmFlag ensures common text/wording when the --path flag is used
+func AddConfirmFlag(cmd *cobra.Command, dflt bool) {
+ cmd.Flags().BoolP("confirm", "c", dflt, "Prompt to confirm options interactively ($DUBBO_CONFIRM)")
+}
+
+// AddPathFlag ensures common text/wording when the --path flag is used
+func AddPathFlag(cmd *cobra.Command) {
+ cmd.Flags().StringP("path", "p", "", "Path to the application. Default is current directory ($DUBBO_PATH)")
+}
+
+// SurveySelectDefault returns 'value' if defined and exists in 'options'.
+// Otherwise, options[0] is returned if it exists. Empty string otherwise.
+//
+// Usage Example:
+//
+// languages := []string{ "go", "node", "rust" },
+// survey.Select{
+// Options: options,
+// Default: surveySelectDefaut(cfg.Language, languages),
+// }
+//
+// Summary:
+//
+// This protects against an incorrectly initialized survey.Select when the user
+// has provided a nonexistant option (validation is handled elsewhere) or
+// when a value is required but there exists no defaults (no default value on
+// the associated flag).
+//
+// Explanation:
+//
+// The above example chooses the default for the Survey (--confirm) question
+// in a way that works with user-provided flag and environment variable values.
+//
+// `cfg.Language` is the current value set in the config struct, which is
+// populated from (in ascending order of precedence):
+// static flag default, associated environment variable, or command flag.
+// `languages` are the options which are being used by the survey select.
+//
+// This cascade allows for the Survey questions to be properly pre-initialzed
+// with their associated environment variables or flags. For example,
+// A user whose default language is set to 'node' using the global environment
+// variable FUNC_LANGUAGE will have that option pre-selected when running
+// `dubbo create -c`.
+//
+// The 'survey' package expects the value of the Default member to exist
+// in the 'Options' member. This is not possible when user-provided data is
+// allowed for the default, hence this logic is necessary.
+//
+// For example, when the user is using prompts (--confirm) to select from a set
+// of options, but the associated flag either has an unrecognized value, or no
+// value at all, without this logic the resulting select prompt would be
+// initialized with this as the default value, and the act of what appears to
+// be choose the first option displayed does not overwrite the invalid default.
+// It could perhaps be argued this is a shortcoming in the survey package, but
+// it is also clearly an error to provide invalid data for a default.
+func SurveySelectDefault(value string, options []string) string {
+ for _, v := range options {
+ if value == v {
+ return v // The provided value is acceptable
+ }
+ }
+ if len(options) > 0 {
+ return options[0] // Sync with the option which will be shown by the UX
+ }
+ // Either the value is not an option or there are no options. Either of
+ // which should fail proper validation
+ return ""
+}
+
+// DeriveNameAndAbsolutePathFromPath returns application name and absolute path
+// to the application project root. The input parameter path could be one of:
+// 'relative/path/to/foo', '/absolute/path/to/foo', 'foo' or ”.
+func DeriveNameAndAbsolutePathFromPath(path string) (string, string) {
+ var absPath string
+
+ // If path is not specified, we would like to use current working dir
+ if path == "" {
+ path = cwd()
+ }
+
+ // Expand the passed function name to its absolute path
+ absPath, err := filepath.Abs(path)
+ if err != nil {
+ return "", ""
+ }
+
+ // Get the name of the function, which equals to name of the current directory
+ pathParts := strings.Split(strings.TrimRight(path, string(os.PathSeparator)), string(os.PathSeparator))
+ return pathParts[len(pathParts)-1], absPath
+}
+
+// cwd returns the current working directory or exits 1 printing the error.
+func cwd() (cwd string) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ panic(fmt.Sprintf("Unable to determine current working directory: %v", err))
+ }
+ return cwd
+}
diff --git a/dubboctl/cmd/dashboard_all_cmds.go b/dubboctl/pkg/dashboard/all.go
similarity index 97%
rename from dubboctl/cmd/dashboard_all_cmds.go
rename to dubboctl/pkg/dashboard/all.go
index 6e31d63..c7c1837 100644
--- a/dubboctl/cmd/dashboard_all_cmds.go
+++ b/dubboctl/pkg/dashboard/all.go
@@ -13,11 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package dashboard
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
"io"
"net"
"os"
@@ -39,8 +41,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/cmd/dashboard.go b/dubboctl/pkg/dashboard/dashboard.go
similarity index 95%
rename from dubboctl/cmd/dashboard.go
rename to dubboctl/pkg/dashboard/dashboard.go
index f2ecc63..46de0d3 100644
--- a/dubboctl/cmd/dashboard.go
+++ b/dubboctl/pkg/dashboard/dashboard.go
@@ -13,13 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package dashboard
import (
"github.com/spf13/cobra"
)
-func addDashboard(rootCmd *cobra.Command) {
+func AddDashboard(rootCmd *cobra.Command) {
dashboardCmd := &cobra.Command{
Use: "dashboard",
Short: "Commands related to control plane components dashboards",
diff --git a/dubboctl/cmd/build.go b/dubboctl/pkg/deploy/build.go
similarity index 80%
rename from dubboctl/cmd/build.go
rename to dubboctl/pkg/deploy/build.go
index 9d6278d..e0956c1 100644
--- a/dubboctl/cmd/build.go
+++ b/dubboctl/pkg/deploy/build.go
@@ -13,10 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+ dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders/dockerfile"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
"strings"
)
@@ -29,20 +34,13 @@
"github.com/spf13/cobra"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/dockerfile"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
-func addBuild(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddBuild(baseCmd *cobra.Command, newClient ClientFactory) {
cmd := &cobra.Command{
Use: "build",
Short: "Build the image for the application",
Long: ``,
SuggestFor: []string{"biuld", "buidl", "built"},
- PreRunE: bindEnv("useDockerfile", "image", "path", "push", "force", "envs",
+ PreRunE: common.BindEnv("useDockerfile", "image", "path", "push", "force", "envs",
"builder-image"),
RunE: func(cmd *cobra.Command, args []string) error {
return runBuildCmd(cmd, newClient)
@@ -61,16 +59,16 @@
"Whether to force build")
cmd.Flags().StringArrayP("envs", "e", []string{},
"environment variable for an application, KEY=VALUE format")
- addPathFlag(cmd)
+ common.AddPathFlag(cmd)
baseCmd.AddCommand(cmd)
}
func runBuildCmd(cmd *cobra.Command, newClient ClientFactory) error {
- if err := util.CreatePaths(); err != nil {
+ if err := util2.CreatePaths(); err != nil {
return err
}
cfg := newBuildConfig(cmd)
- f, err := dubbo.NewDubbo(cfg.Path)
+ f, err := dubbo2.NewDubbo(cfg.Path)
if err != nil {
return err
}
@@ -80,7 +78,7 @@
return err
}
if !f.Initialized() {
- return dubbo.NewErrNotInitialized(f.Root)
+ return dubbo2.NewErrNotInitialized(f.Root)
}
cfg.Configure(f)
@@ -110,9 +108,9 @@
return nil
}
-func (c *buildConfig) Prompt(d *dubbo.Dubbo) (*buildConfig, error) {
+func (c *buildConfig) Prompt(d *dubbo2.Dubbo) (*buildConfig, error) {
var err error
- if !util.InteractiveTerminal() {
+ if !util2.InteractiveTerminal() {
return c, nil
}
@@ -135,13 +133,13 @@
return c, err
}
-func (c buildConfig) buildclientOptions() ([]dubbo.Option, error) {
- var o []dubbo.Option
+func (c buildConfig) buildclientOptions() ([]dubbo2.Option, error) {
+ var o []dubbo2.Option
if c.UseDockerfile {
- o = append(o, dubbo.WithBuilder(dockerfile.NewBuilder()))
+ o = append(o, dubbo2.WithBuilder(dockerfile.NewBuilder()))
} else {
- o = append(o, dubbo.WithBuilder(pack.NewBuilder()))
+ o = append(o, dubbo2.WithBuilder(pack.NewBuilder()))
}
return o, nil
@@ -181,7 +179,7 @@
return c
}
-func (c *buildConfig) Configure(f *dubbo.Dubbo) {
+func (c *buildConfig) Configure(f *dubbo2.Dubbo) {
if c.Path == "" {
root, err := os.Getwd()
if err != nil {
@@ -209,9 +207,9 @@
envs[parts[0]] = parts[1]
}
}
- f.Build.BuildEnvs = make([]dubbo.Env, 0, len(envs))
+ f.Build.BuildEnvs = make([]dubbo2.Env, 0, len(envs))
for k, v := range envs {
- f.Build.BuildEnvs = append(f.Build.BuildEnvs, dubbo.Env{
+ f.Build.BuildEnvs = append(f.Build.BuildEnvs, dubbo2.Env{
Name: &k,
Value: &v,
})
diff --git a/dubboctl/cmd/client.go b/dubboctl/pkg/deploy/client.go
similarity index 71%
rename from dubboctl/cmd/client.go
rename to dubboctl/pkg/deploy/client.go
index 71894a8..f9d8f41 100644
--- a/dubboctl/cmd/client.go
+++ b/dubboctl/pkg/deploy/client.go
@@ -13,57 +13,54 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
+ dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ dubbohttp "github.com/apache/dubbo-kubernetes/operator/http"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker/prompt"
+ config "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"net/http"
"os"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/cmd/prompt"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- dubbohttp "github.com/apache/dubbo-kubernetes/dubboctl/internal/http"
- config "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// ClientFactory defines a constructor which assists in the creation of a Client
// for use by commands.
// See the NewClient constructor which is the fully populated ClientFactory used
// by commands by default.
// See NewClientFactory which constructs a minimal ClientFactory for use
// during testing.
-type ClientFactory func(...dubbo.Option) (*dubbo.Client, func())
+type ClientFactory func(...dubbo2.Option) (*dubbo2.Client, func())
-func NewClient(options ...dubbo.Option) (*dubbo.Client, func()) {
+func NewClient(options ...dubbo2.Option) (*dubbo2.Client, func()) {
var (
t = newTransport(false)
c = newCredentialsProvider(config.Dir(), t)
d = newDubboDeployer()
- o = []dubbo.Option{
- dubbo.WithRepositoriesPath(config.RepositoriesPath()),
- dubbo.WithBuilder(pack.NewBuilder()),
- dubbo.WithPusher(docker.NewPusher(
+ o = []dubbo2.Option{
+ dubbo2.WithRepositoriesPath(config.RepositoriesPath()),
+ dubbo2.WithBuilder(pack.NewBuilder()),
+ dubbo2.WithPusher(docker.NewPusher(
docker.WithCredentialsProvider(c),
docker.WithTransport(t))),
- dubbo.WithDeployer(d),
+ dubbo2.WithDeployer(d),
}
)
// Client is constructed with standard options plus any additional options
// which either augment or override the defaults.
- client := dubbo.New(append(o, options...)...)
+ client := dubbo2.New(append(o, options...)...)
cleanup := func() {}
return client, cleanup
}
-func newDubboDeployer() dubbo.Deployer {
- var options []dubbo.DeployerOpt
+func newDubboDeployer() dubbo2.Deployer {
+ var options []dubbo2.DeployerOpt
- return dubbo.NewDeployer(options...)
+ return dubbo2.NewDeployer(options...)
}
// newTransport returns a transport with cluster-flavor-specific variations
diff --git a/dubboctl/cmd/create.go b/dubboctl/pkg/deploy/create.go
similarity index 89%
rename from dubboctl/cmd/create.go
rename to dubboctl/pkg/deploy/create.go
index b3ff738..605124c 100644
--- a/dubboctl/cmd/create.go
+++ b/dubboctl/pkg/deploy/create.go
@@ -13,11 +13,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+ dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
"strings"
"text/tabwriter"
@@ -32,11 +35,6 @@
"github.com/spf13/cobra"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// ErrNoRuntime indicates that the language runtime flag was not passed.
type ErrNoRuntime error
@@ -47,7 +45,7 @@
type ErrInvalidTemplate error
// NewCreateCmd creates a create command using the given client creator.
-func addCreate(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddCreate(baseCmd *cobra.Command, newClient ClientFactory) {
cmd := &cobra.Command{
Use: "create",
Short: "Create an application",
@@ -94,7 +92,7 @@
$ {{.Name}} create -l go -t common mydubbo
`,
SuggestFor: []string{"vreate", "creaet", "craete", "new"},
- PreRunE: bindEnv("language", "template", "repository", "confirm", "init"),
+ PreRunE: common.BindEnv("language", "template", "repository", "confirm", "init"),
Aliases: []string{"init"},
RunE: func(cmd *cobra.Command, args []string) error {
return runCreate(cmd, args, newClient)
@@ -108,7 +106,7 @@
cmd.Flags().BoolP("init", "i", false,
"Initialize the current project directly into a dubbo project without using a template")
- addConfirmFlag(cmd, false)
+ common.AddConfirmFlag(cmd, false)
// Help Action
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { runCreateHelp(cmd, args, newClient) })
@@ -138,7 +136,7 @@
// From environment variables, flags, arguments, and user prompts if --confirm
// (in increasing levels of precedence)
client, done := newClient(
- dubbo.WithRepository(cfg.Repository))
+ dubbo2.WithRepository(cfg.Repository))
defer done()
// Validate - a deeper validation than that which is performed when
@@ -148,13 +146,13 @@
}
// Create
- _, err = client.Init(&dubbo.Dubbo{
+ _, err = client.Init(&dubbo2.Dubbo{
Name: cfg.Name,
Root: cfg.Path,
Runtime: cfg.Runtime,
Template: cfg.Template,
- Build: dubbo.BuildSpec{
- CnMirror: dubbo.BooleanWithComment{
+ Build: dubbo2.BuildSpec{
+ CnMirror: dubbo2.BooleanWithComment{
Comment: "Specify `cnMirror: true` to use the mirror in mainland China",
},
},
@@ -199,7 +197,7 @@
path = args[0]
}
- dirName, absolutePath = deriveNameAndAbsolutePathFromPath(path)
+ dirName, absolutePath = common.DeriveNameAndAbsolutePathFromPath(path)
// Config is the final default values based off the execution context.
// When prompting, these become the defaults presented.
@@ -223,7 +221,7 @@
defer done()
// IN confirm mode. If also in an interactive terminal, run prompts.
- if util.InteractiveTerminal() {
+ if util2.InteractiveTerminal() {
createdCfg, err := cfg.prompt(client)
if err != nil {
return createdCfg, err
@@ -270,9 +268,9 @@
// can be used to determine possible values for runtime, templates, etc. a
// pre-client validation should not be required, as the Client does its own
// validation.
-func (c createConfig) Validate(client *dubbo.Client) (err error) {
- dirName, _ := deriveNameAndAbsolutePathFromPath(c.Path)
- if err = util.ValidateApplicationName(dirName); err != nil {
+func (c createConfig) Validate(client *dubbo2.Client) (err error) {
+ dirName, _ := common.DeriveNameAndAbsolutePathFromPath(c.Path)
+ if err = util2.ValidateApplicationName(dirName); err != nil {
return
}
@@ -293,7 +291,7 @@
}
// isValidRuntime determines if the given language runtime is a valid choice.
-func isValidRuntime(client *dubbo.Client, runtime string) bool {
+func isValidRuntime(client *dubbo2.Client, runtime string) bool {
runtimes, err := client.Runtimes()
if err != nil {
return false
@@ -308,7 +306,7 @@
// isValidTemplate determines if the given template is valid for the given
// runtime.
-func isValidTemplate(client *dubbo.Client, runtime, template string) bool {
+func isValidTemplate(client *dubbo2.Client, runtime, template string) bool {
if !isValidRuntime(client, runtime) {
return false
}
@@ -324,7 +322,7 @@
return false
}
-func noRuntimeError(client *dubbo.Client) error {
+func noRuntimeError(client *dubbo2.Client) error {
b := strings.Builder{}
fmt.Fprintln(&b, "Required flag \"language\" not set.")
fmt.Fprintln(&b, "Available language runtimes are:")
@@ -338,7 +336,7 @@
return ErrNoRuntime(errors.New(b.String()))
}
-func newInvalidRuntimeError(client *dubbo.Client, runtime string) error {
+func newInvalidRuntimeError(client *dubbo2.Client, runtime string) error {
b := strings.Builder{}
fmt.Fprintf(&b, "The language runtime '%v' is not recognized.\n", runtime)
fmt.Fprintln(&b, "Available language runtimes are:")
@@ -352,7 +350,7 @@
return ErrInvalidRuntime(errors.New(b.String()))
}
-func newInvalidTemplateError(client *dubbo.Client, runtime, template string) error {
+func newInvalidTemplateError(client *dubbo2.Client, runtime, template string) error {
b := strings.Builder{}
fmt.Fprintf(&b, "The template '%v' was not found for language runtime '%v'.\n", template, runtime)
fmt.Fprintln(&b, "Available templates for this language runtime are:")
@@ -370,7 +368,7 @@
// mutating the values. The provided clientFn is used to construct a transient
// client for use during prompt autocompletion/suggestions (such as suggesting
// valid templates)
-func (c createConfig) prompt(client *dubbo.Client) (createConfig, error) {
+func (c createConfig) prompt(client *dubbo2.Client) (createConfig, error) {
var qs []*survey.Question
runtimes, err := client.Runtimes()
@@ -403,11 +401,11 @@
Default: c.Path,
},
Validate: func(val interface{}) error {
- derivedName, _ := deriveNameAndAbsolutePathFromPath(val.(string))
- return util.ValidateApplicationName(derivedName)
+ derivedName, _ := common.DeriveNameAndAbsolutePathFromPath(val.(string))
+ return util2.ValidateApplicationName(derivedName)
},
Transform: func(ans interface{}) interface{} {
- _, absolutePath := deriveNameAndAbsolutePathFromPath(ans.(string))
+ _, absolutePath := common.DeriveNameAndAbsolutePathFromPath(ans.(string))
return absolutePath
},
}, {
@@ -415,7 +413,7 @@
Prompt: &survey.Select{
Message: "Language Runtime:",
Options: runtimes,
- Default: surveySelectDefault(c.Runtime, runtimes),
+ Default: common.SurveySelectDefault(c.Runtime, runtimes),
},
},
}
@@ -479,7 +477,7 @@
// return templates for language runtime whose full name (including repository)
// have the given prefix.
-func templatesWithPrefix(prefix, runtime string, client *dubbo.Client) ([]string, error) {
+func templatesWithPrefix(prefix, runtime string, client *dubbo2.Client) ([]string, error) {
var (
suggestions []string
templates, err = client.Templates().List(runtime)
@@ -510,7 +508,7 @@
failSoft(err)
client, done := newClient(
- dubbo.WithRepository(cfg.Repository))
+ dubbo2.WithRepository(cfg.Repository))
defer done()
options, err := RuntimeTemplateOptions(client) // human-friendly
@@ -546,7 +544,7 @@
// RuntimeTemplateOptions is a human-friendly table of valid Language Runtime
// to Template combinations.
// Exported for use in docs.
-func RuntimeTemplateOptions(client *dubbo.Client) (string, error) {
+func RuntimeTemplateOptions(client *dubbo2.Client) (string, error) {
runtimes, err := client.Runtimes()
if err != nil {
return "", err
@@ -560,7 +558,7 @@
templates, err := client.Templates().List(r)
// Not all language packs will have templates for
// all available runtimes. Without this check
- if err != nil && !errors.Is(err, dubbo.ErrTemplateNotFound) {
+ if err != nil && !errors.Is(err, dubbo2.ErrTemplateNotFound) {
return "", err
}
for _, t := range templates {
diff --git a/dubboctl/cmd/create_test.go b/dubboctl/pkg/deploy/create_test.go
similarity index 65%
rename from dubboctl/cmd/create_test.go
rename to dubboctl/pkg/deploy/create_test.go
index 4c8974b..fe2c2eb 100644
--- a/dubboctl/cmd/create_test.go
+++ b/dubboctl/pkg/deploy/create_test.go
@@ -13,15 +13,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy_test
import (
"errors"
+ "github.com/apache/dubbo-kubernetes/dubboctl/cmd"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"testing"
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
+ "github.com/ory/viper"
)
// TestCreate_Execute ensures that an invocation of create with minimal settings
@@ -29,9 +33,9 @@
func TestCreate_Execute(t *testing.T) {
_ = fromTempDirectory(t)
- cmd := getRootCmd([]string{"create", "--language", "go", "myfunc"})
+ cmds := cmd.GetRootCmd([]string{"create", "--language", "go", "myfunc"})
- if err := cmd.Execute(); err != nil {
+ if err := cmds.Execute(); err != nil {
t.Fatal(err)
}
}
@@ -41,10 +45,10 @@
func TestCreate_NoRuntime(t *testing.T) {
_ = fromTempDirectory(t)
- cmd := getRootCmd([]string{"create", "myfunc"})
+ cmds := cmd.GetRootCmd([]string{"create", "myfunc"})
- err := cmd.Execute()
- var e ErrNoRuntime
+ err := cmds.Execute()
+ var e deploy.ErrNoRuntime
if !errors.As(err, &e) {
t.Fatalf("Did not receive ErrNoRuntime. Got %v", err)
}
@@ -55,10 +59,10 @@
func TestCreate_WithInvalidRuntime(t *testing.T) {
_ = fromTempDirectory(t)
- cmd := getRootCmd([]string{"create", "--language", "invalid", "myfunc"})
+ cmds := cmd.GetRootCmd([]string{"create", "--language", "invalid", "myfunc"})
- err := cmd.Execute()
- var e ErrInvalidRuntime
+ err := cmds.Execute()
+ var e deploy.ErrInvalidRuntime
if !errors.As(err, &e) {
t.Fatalf("Did not receive ErrInvalidRuntime. Got %v", err)
}
@@ -69,10 +73,10 @@
func TestCreate_InvalidTemplate(t *testing.T) {
_ = fromTempDirectory(t)
- cmd := getRootCmd([]string{"create", "--language", "go", "--template", "invalid", "myfunc"})
+ cmds := cmd.GetRootCmd([]string{"create", "--language", "go", "--template", "invalid", "myfunc"})
- err := cmd.Execute()
- var e ErrInvalidTemplate
+ err := cmds.Execute()
+ var e deploy.ErrInvalidTemplate
if !errors.As(err, &e) {
t.Fatalf("Did not receive ErrInvalidTemplate. Got %v", err)
}
@@ -85,8 +89,8 @@
// Execute the command with a function name containing invalid characters and
// confirm the expected error is returned
- cmd := getRootCmd([]string{"create", "invalid!"})
- err := cmd.Execute()
+ cmds := cmd.GetRootCmd([]string{"create", "invalid!"})
+ err := cmds.Execute()
var e util.ErrInvalidApplicationName
if !errors.As(err, &e) {
t.Fatalf("Did not receive ErrInvalidApplicationName. Got %v", err)
@@ -100,11 +104,28 @@
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
- cmd := getRootCmd([]string{"create", "--language=go", "myfunc"})
- if err := cmd.Execute(); err != nil {
+ cmds := cmd.GetRootCmd([]string{"create", "--language=go", "myfunc"})
+ if err := cmds.Execute(); err != nil {
t.Fatal(err)
}
// Not failing is success. Config files or settings beyond what are
// automatically written to to the given config home are currently optional.
}
+
+// fromTempDirectory is a test helper which endeavors to create
+// an environment clean of developer's settings for use during CLI testing.
+func fromTempDirectory(t *testing.T) string {
+ t.Helper()
+ ClearEnvs(t)
+
+ // By default unit tests presume no config exists unless provided in testdata.
+ t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+ // creates and CDs to a temp directory
+ d, done := Mktemp(t)
+
+ // Return to original directory and resets viper.
+ t.Cleanup(func() { done(); viper.Reset() })
+ return d
+}
diff --git a/dubboctl/cmd/deploy.go b/dubboctl/pkg/deploy/deploy.go
similarity index 89%
rename from dubboctl/cmd/deploy.go
rename to dubboctl/pkg/deploy/deploy.go
index 2d27385..4f04fa2 100644
--- a/dubboctl/cmd/deploy.go
+++ b/dubboctl/pkg/deploy/deploy.go
@@ -13,11 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+ dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
"os/exec"
"path/filepath"
@@ -37,18 +41,12 @@
"k8s.io/client-go/util/homedir"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
const (
basePort = 30000
portLimit = 32767
)
-func addDeploy(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddDeploy(baseCmd *cobra.Command, newClient ClientFactory) {
cmd := &cobra.Command{
Use: "deploy",
Short: "Generate the k8s yaml of the application. By the way, you can choose to build the image, push the image and apply to the k8s cluster.",
@@ -60,7 +58,7 @@
dubboctl deploy [flags]
`,
SuggestFor: []string{"delpoy", "deplyo"},
- PreRunE: bindEnv("path", "output", "namespace", "image", "envs", "name", "containerPort",
+ PreRunE: common.BindEnv("path", "output", "namespace", "image", "envs", "name", "containerPort",
"targetPort", "nodePort", "apply", "useDockerfile", "force", "builder-image", "build", "context",
"kubeConfig", "push"),
RunE: func(cmd *cobra.Command, args []string) error {
@@ -106,17 +104,17 @@
cmd.Flags().StringP("portName", "", "http",
"Name of the port to be exposed")
- addPathFlag(cmd)
+ common.AddPathFlag(cmd)
cmd.Flags().SetInterspersed(false)
baseCmd.AddCommand(cmd)
}
func runDeploy(cmd *cobra.Command, newClient ClientFactory) error {
- if err := util.CreatePaths(); err != nil {
+ if err := util2.CreatePaths(); err != nil {
return err
}
cfg := newDeployConfig(cmd)
- f, err := dubbo.NewDubbo(cfg.Path)
+ f, err := dubbo2.NewDubbo(cfg.Path)
if err != nil {
return err
}
@@ -129,7 +127,7 @@
}
if !f.Initialized() {
- return dubbo.NewErrNotInitialized(f.Root)
+ return dubbo2.NewErrNotInitialized(f.Root)
}
cfg.Configure(f)
@@ -198,7 +196,7 @@
return nil
}
-func (d DeployConfig) deployclientOptions() ([]dubbo.Option, error) {
+func (d DeployConfig) deployclientOptions() ([]dubbo2.Option, error) {
o, err := d.buildclientOptions()
if err != nil {
return o, err
@@ -212,11 +210,11 @@
if err != nil {
return o, err
}
- o = append(o, dubbo.WithKubeClient(cli))
+ o = append(o, dubbo2.WithKubeClient(cli))
return o, nil
}
-func applyTok8s(cmd *cobra.Command, d *dubbo.Dubbo) error {
+func applyTok8s(cmd *cobra.Command, d *dubbo2.Dubbo) error {
file := filepath.Join(d.Root, d.Deploy.Output)
c := exec.CommandContext(cmd.Context(), "kubectl", "apply", "-f", file)
c.Stdout = os.Stdout
@@ -233,9 +231,9 @@
return nil
}
-func (c *DeployConfig) Prompt(d *dubbo.Dubbo) (*DeployConfig, error) {
+func (c *DeployConfig) Prompt(d *dubbo2.Dubbo) (*DeployConfig, error) {
var err error
- if !util.InteractiveTerminal() {
+ if !util2.InteractiveTerminal() {
return c, nil
}
buildconfig, err := c.buildConfig.Prompt(d)
@@ -261,7 +259,7 @@
return c, err
}
-func (c DeployConfig) Configure(f *dubbo.Dubbo) {
+func (c DeployConfig) Configure(f *dubbo2.Dubbo) {
c.buildConfig.Configure(f)
if c.Namespace != "" {
f.Deploy.Namespace = c.Namespace
diff --git a/dubboctl/cmd/repository.go b/dubboctl/pkg/deploy/repository.go
similarity index 94%
rename from dubboctl/cmd/repository.go
rename to dubboctl/pkg/deploy/repository.go
index 2714d5f..7384505 100644
--- a/dubboctl/cmd/repository.go
+++ b/dubboctl/pkg/deploy/repository.go
@@ -13,11 +13,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
)
@@ -29,14 +32,9 @@
"github.com/spf13/cobra"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// command constructors
// --------------------
-func addRepository(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddRepository(baseCmd *cobra.Command, newClient ClientFactory) {
cmd := &cobra.Command{
Short: "Manage installed template repositories",
Use: "repository",
@@ -165,13 +163,13 @@
default
`,
SuggestFor: []string{"repositories", "repos", "template", "templates", "pack", "packs"},
- PreRunE: bindEnv("confirm"),
+ PreRunE: common.BindEnv("confirm"),
RunE: func(cmd *cobra.Command, args []string) error {
return runRepository(cmd, args, newClient)
},
}
- addConfirmFlag(cmd, false)
+ common.AddConfirmFlag(cmd, false)
cmd.AddCommand(NewRepositoryListCmd(newClient))
cmd.AddCommand(NewRepositoryAddCmd(newClient))
@@ -182,64 +180,64 @@
}
func NewRepositoryListCmd(newClient ClientFactory) *cobra.Command {
- cmd := &cobra.Command{
+ cmds := &cobra.Command{
Short: "List repositories",
Use: "list",
Aliases: []string{"ls"},
- PreRunE: bindEnv("confirm"),
+ PreRunE: common.BindEnv("confirm"),
RunE: func(cmd *cobra.Command, args []string) error {
return runRepositoryList(cmd, args, newClient)
},
}
- addConfirmFlag(cmd, false)
- return cmd
+ common.AddConfirmFlag(cmds, false)
+ return cmds
}
func NewRepositoryAddCmd(newClient ClientFactory) *cobra.Command {
- cmd := &cobra.Command{
+ cmds := &cobra.Command{
Short: "Add a repository",
Use: "add <name> <url>",
SuggestFor: []string{"ad", "install"},
- PreRunE: bindEnv("confirm"),
+ PreRunE: common.BindEnv("confirm"),
RunE: func(cmd *cobra.Command, args []string) error {
return runRepositoryAdd(cmd, args, newClient)
},
}
- addConfirmFlag(cmd, false)
- return cmd
+ common.AddConfirmFlag(cmds, false)
+ return cmds
}
func NewRepositoryRenameCmd(newClient ClientFactory) *cobra.Command {
- cmd := &cobra.Command{
+ cmds := &cobra.Command{
Short: "Rename a repository",
Use: "rename <old> <new>",
Aliases: []string{"mv"},
- PreRunE: bindEnv("confirm"),
+ PreRunE: common.BindEnv("confirm"),
RunE: func(cmd *cobra.Command, args []string) error {
return runRepositoryRename(cmd, args, newClient)
},
}
- addConfirmFlag(cmd, false)
- return cmd
+ common.AddConfirmFlag(cmds, false)
+ return cmds
}
func NewRepositoryRemoveCmd(newClient ClientFactory) *cobra.Command {
- cmd := &cobra.Command{
+ cmds := &cobra.Command{
Short: "Remove a repository",
Use: "remove <name>",
Aliases: []string{"rm"},
SuggestFor: []string{"delete", "del"},
- PreRunE: bindEnv("confirm"),
+ PreRunE: common.BindEnv("confirm"),
RunE: func(cmd *cobra.Command, args []string) error {
return runRepositoryRemove(cmd, args, newClient)
},
}
- addConfirmFlag(cmd, false)
- return cmd
+ common.AddConfirmFlag(cmds, false)
+ return cmds
}
// command implementations
@@ -324,7 +322,7 @@
}
// Adding a repository requires there be a config path structure on disk
- if err = util.CreatePaths(); err != nil {
+ if err = util2.CreatePaths(); err != nil {
return
}
@@ -368,7 +366,7 @@
// This is an expected case. The empty value will be echoed to stdout, the
// API will be invoked, and a helpful error message will indicate that the
// request is missing required parameters.
- if cfg.Confirm && util.InteractiveTerminal() {
+ if cfg.Confirm && util2.InteractiveTerminal() {
questions := []*survey.Question{
{
Name: "Name",
@@ -439,7 +437,7 @@
}
// Confirm (interactive prompt mode)
- if cfg.Confirm && util.InteractiveTerminal() {
+ if cfg.Confirm && util2.InteractiveTerminal() {
questions := []*survey.Question{
{
Name: "Old",
@@ -515,7 +513,7 @@
}
// Confirm (interactive prompt mode)
- if cfg.Confirm && util.InteractiveTerminal() {
+ if cfg.Confirm && util2.InteractiveTerminal() {
questions := []*survey.Question{
{
Name: "Name",
diff --git a/dubboctl/cmd/root_test.go b/dubboctl/pkg/deploy/repository_test.go
similarity index 67%
rename from dubboctl/cmd/root_test.go
rename to dubboctl/pkg/deploy/repository_test.go
index 294b955..095c148 100644
--- a/dubboctl/cmd/root_test.go
+++ b/dubboctl/pkg/deploy/repository_test.go
@@ -13,38 +13,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy_test
import (
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+ "github.com/ory/viper"
"io"
"os"
"strings"
"testing"
)
-import (
- "github.com/ory/viper"
-)
+// TestRepository_List ensures that the 'list' subcommand shows the client's
+// set of repositories by name for builtin repositories, by explicitly
+// setting the repositories' path to a new path which includes no others.
+func TestRepository_List(t *testing.T) {
+ _ = FromTempDirectory(t)
-import (
- . "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
+ cmd := deploy.NewRepositoryListCmd(deploy.NewClient)
+ cmd.SetArgs([]string{}) // Do not use test command args
-// fromTempDirectory is a test helper which endeavors to create
-// an environment clean of developer's settings for use during CLI testing.
-func fromTempDirectory(t *testing.T) string {
- t.Helper()
- ClearEnvs(t)
+ // Execute the command, capturing the output sent to stdout
+ stdout := piped(t)
+ if err := cmd.Execute(); err != nil {
+ t.Fatal(err)
+ }
- // By default unit tests presume no config exists unless provided in testdata.
- t.Setenv("XDG_CONFIG_HOME", t.TempDir())
-
- // creates and CDs to a temp directory
- d, done := Mktemp(t)
-
- // Return to original directory and resets viper.
- t.Cleanup(func() { done(); viper.Reset() })
- return d
+ // Assert the output matches expect (whitespace trimmed)
+ expect := "default"
+ output := stdout()
+ if output != expect {
+ t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output)
+ }
}
// pipe the output of stdout to a buffer whose value is returned
@@ -82,3 +83,20 @@
return strings.TrimSpace(b.String())
}
}
+
+// fromTempDirectory is a test helper which endeavors to create
+// an environment clean of developer's settings for use during CLI testing.
+func FromTempDirectory(t *testing.T) string {
+ t.Helper()
+ ClearEnvs(t)
+
+ // By default unit tests presume no config exists unless provided in testdata.
+ t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+ // creates and CDs to a temp directory
+ d, done := Mktemp(t)
+
+ // Return to original directory and resets viper.
+ t.Cleanup(func() { done(); viper.Reset() })
+ return d
+}
diff --git a/dubboctl/cmd/completion_util.go b/dubboctl/pkg/deploy/util.go
similarity index 95%
rename from dubboctl/cmd/completion_util.go
rename to dubboctl/pkg/deploy/util.go
index 96f4d8c..017364d 100644
--- a/dubboctl/cmd/completion_util.go
+++ b/dubboctl/pkg/deploy/util.go
@@ -13,10 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package deploy
import (
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
"os"
"strings"
)
@@ -25,10 +26,6 @@
"github.com/spf13/cobra"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
func CompleteRuntimeList(cmd *cobra.Command, args []string, toComplete string, client *dubbo.Client) (matches []string, directive cobra.ShellCompDirective) {
runtimes, err := client.Runtimes()
if err != nil {
diff --git a/dubboctl/cmd/generate_certificate.go b/dubboctl/pkg/generate/certificate.go
similarity index 99%
rename from dubboctl/cmd/generate_certificate.go
rename to dubboctl/pkg/generate/certificate.go
index b3fe333..b55a4b6 100644
--- a/dubboctl/cmd/generate_certificate.go
+++ b/dubboctl/pkg/generate/certificate.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package generate
import (
"fmt"
diff --git a/dubboctl/cmd/generate.go b/dubboctl/pkg/generate/generate.go
similarity index 94%
rename from dubboctl/cmd/generate.go
rename to dubboctl/pkg/generate/generate.go
index 5c2bc6e..ae76936 100644
--- a/dubboctl/cmd/generate.go
+++ b/dubboctl/pkg/generate/generate.go
@@ -13,13 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package generate
import (
"github.com/spf13/cobra"
)
-func addGenerate(rootCmd *cobra.Command) {
+func AddGenerate(rootCmd *cobra.Command) {
generateCmd := &cobra.Command{
Use: "generate",
Short: "Generate resources, tokens, etc",
diff --git a/dubboctl/cmd/generate.go b/dubboctl/pkg/manifest/common.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to dubboctl/pkg/manifest/common.go
index 5c2bc6e..8886181 100644
--- a/dubboctl/cmd/generate.go
+++ b/dubboctl/pkg/manifest/common.go
@@ -13,18 +13,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package manifest
import (
- "github.com/spf13/cobra"
+ "sigs.k8s.io/controller-runtime/pkg/client"
)
-func addGenerate(rootCmd *cobra.Command) {
- generateCmd := &cobra.Command{
- Use: "generate",
- Short: "Generate resources, tokens, etc",
- Long: `Generate resources, tokens, etc.`,
- }
- rootCmd.AddCommand(generateCmd)
- NewGenerateCertificateCmd(generateCmd)
-}
+var (
+ // TestInstallFlag and TestCli are uses for black box testing
+ TestInstallFlag bool
+ TestCli client.Client
+)
diff --git a/dubboctl/cmd/manifest_diff.go b/dubboctl/pkg/manifest/diff.go
similarity index 97%
rename from dubboctl/cmd/manifest_diff.go
rename to dubboctl/pkg/manifest/diff.go
index b1942ff..b62981c 100644
--- a/dubboctl/cmd/manifest_diff.go
+++ b/dubboctl/pkg/manifest/diff.go
@@ -13,11 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package manifest
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
"os"
"path/filepath"
"strings"
@@ -30,7 +31,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/cmd/manifest_generate.go b/dubboctl/pkg/manifest/generate.go
similarity index 94%
rename from dubboctl/cmd/manifest_generate.go
rename to dubboctl/pkg/manifest/generate.go
index fd69e96..6e5431f 100644
--- a/dubboctl/cmd/manifest_generate.go
+++ b/dubboctl/pkg/manifest/generate.go
@@ -13,10 +13,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package manifest
import (
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/manifest"
+ "github.com/apache/dubbo-kubernetes/operator/manifest/render"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
"path"
"sort"
@@ -32,12 +38,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest/render"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/cmd/manifest_install.go b/dubboctl/pkg/manifest/install.go
similarity index 86%
rename from dubboctl/cmd/manifest_install.go
rename to dubboctl/pkg/manifest/install.go
index a767193..972be59 100644
--- a/dubboctl/cmd/manifest_install.go
+++ b/dubboctl/pkg/manifest/install.go
@@ -13,17 +13,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package manifest
import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
@@ -74,14 +74,14 @@
func installManifests(miArgs *ManifestInstallArgs, cfg *v1alpha1.DubboConfig) error {
var cliOpts []kube.CtlClientOption
- if TestInstallFlag {
- cliOpts = []kube.CtlClientOption{kube.WithCli(TestCli)}
- } else {
- cliOpts = []kube.CtlClientOption{
- kube.WithKubeConfigPath(miArgs.KubeConfigPath),
- kube.WithContext(miArgs.Context),
- }
- }
+ //if cmd.TestInstallFlag {
+ // cliOpts = []kube.CtlClientOption{kube.WithCli(cmd.TestCli)}
+ //} else {
+ // cliOpts = []kube.CtlClientOption{
+ // kube.WithKubeConfigPath(miArgs.KubeConfigPath),
+ // kube.WithContext(miArgs.Context),
+ // }
+ //}
cli, err := kube.NewCtlClient(cliOpts...)
if err != nil {
return err
diff --git a/dubboctl/cmd/manifest_uninstall.go b/dubboctl/pkg/manifest/uninstall.go
similarity index 86%
rename from dubboctl/cmd/manifest_uninstall.go
rename to dubboctl/pkg/manifest/uninstall.go
index 8d10215..5fefbb3 100644
--- a/dubboctl/cmd/manifest_uninstall.go
+++ b/dubboctl/pkg/manifest/uninstall.go
@@ -13,17 +13,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package manifest
import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
@@ -71,14 +71,14 @@
func uninstallManifests(muArgs *ManifestUninstallArgs, cfg *v1alpha1.DubboConfig) error {
var cliOpts []kube.CtlClientOption
- if TestInstallFlag {
- cliOpts = []kube.CtlClientOption{kube.WithCli(TestCli)}
- } else {
- cliOpts = []kube.CtlClientOption{
- kube.WithKubeConfigPath(muArgs.KubeConfigPath),
- kube.WithContext(muArgs.Context),
- }
- }
+ //if cmd.TestInstallFlag {
+ // cliOpts = []kube.CtlClientOption{kube.WithCli(cmd.TestCli)}
+ //} else {
+ // cliOpts = []kube.CtlClientOption{
+ // kube.WithKubeConfigPath(muArgs.KubeConfigPath),
+ // kube.WithContext(muArgs.Context),
+ // }
+ //}
cli, err := kube.NewCtlClient(cliOpts...)
if err != nil {
return err
diff --git a/dubboctl/cmd/profile_diff.go b/dubboctl/pkg/profile/diff.go
similarity index 93%
rename from dubboctl/cmd/profile_diff.go
rename to dubboctl/pkg/profile/diff.go
index 8080601..2b80d3a 100644
--- a/dubboctl/cmd/profile_diff.go
+++ b/dubboctl/pkg/profile/diff.go
@@ -13,11 +13,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package profile
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/manifest"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
)
import (
@@ -27,9 +30,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/cmd/profile_list.go b/dubboctl/pkg/profile/list.go
similarity index 93%
rename from dubboctl/cmd/profile_list.go
rename to dubboctl/pkg/profile/list.go
index 695f1a7..8971694 100644
--- a/dubboctl/cmd/profile_list.go
+++ b/dubboctl/pkg/profile/list.go
@@ -13,10 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package profile
import (
"errors"
+ "github.com/apache/dubbo-kubernetes/operator/manifest"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"strings"
)
@@ -27,9 +30,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/cmd/proxy_context.go b/dubboctl/pkg/proxy/context.go
similarity index 97%
rename from dubboctl/cmd/proxy_context.go
rename to dubboctl/pkg/proxy/context.go
index 4de6264..69eeb57 100644
--- a/dubboctl/cmd/proxy_context.go
+++ b/dubboctl/pkg/proxy/context.go
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-package cmd
+package proxy
import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/envoy"
"os"
"path/filepath"
"runtime"
@@ -33,7 +34,6 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/envoy"
"github.com/apache/dubbo-kubernetes/pkg/config/app/dubboctl"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
diff --git a/dubboctl/cmd/proxy.go b/dubboctl/pkg/proxy/proxy.go
similarity index 97%
rename from dubboctl/cmd/proxy.go
rename to dubboctl/pkg/proxy/proxy.go
index 98f2918..3247c72 100644
--- a/dubboctl/cmd/proxy.go
+++ b/dubboctl/pkg/proxy/proxy.go
@@ -15,11 +15,13 @@
* limitations under the License.
*/
-package cmd
+package proxy
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/envoy"
"io"
"os"
"path/filepath"
@@ -36,7 +38,6 @@
import (
mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/envoy"
"github.com/apache/dubbo-kubernetes/pkg/config/app/dubboctl"
"github.com/apache/dubbo-kubernetes/pkg/core"
dubbo_cmd "github.com/apache/dubbo-kubernetes/pkg/core/cmd"
@@ -50,7 +51,7 @@
"github.com/apache/dubbo-kubernetes/pkg/util/template"
)
-var runLog = controlPlaneLog.WithName("proxy")
+var runLog = common.ControlPlaneLog.WithName("proxy")
type ResourceType string
@@ -95,7 +96,7 @@
return os.WriteFile(filename, data, perm)
}
-func addProxy(opts dubbo_cmd.RunCmdOpts, cmd *cobra.Command) {
+func AddProxy(opts dubbo_cmd.RunCmdOpts, cmd *cobra.Command) {
proxyArgs := DefaultProxyConfig()
cfg := proxyArgs.Config
var proxyResource model.Resource
diff --git a/dubboctl/cmd/registry.go b/dubboctl/pkg/registry/registry.go
similarity index 95%
rename from dubboctl/cmd/registry.go
rename to dubboctl/pkg/registry/registry.go
index b47f649..f7ca561 100644
--- a/dubboctl/cmd/registry.go
+++ b/dubboctl/pkg/registry/registry.go
@@ -13,17 +13,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package registry
import (
+ "github.com/apache/dubbo-kubernetes/operator/registry/zk"
"github.com/spf13/cobra"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/registry/zk"
-)
-
-func addRegistryCmd(rootCmd *cobra.Command) {
+func AddRegistryCmd(rootCmd *cobra.Command) {
addZkRegistryCmd(rootCmd)
}
diff --git a/dubboctl/templates/go/README.md b/dubboctl/pkg/templates/go/README.md
similarity index 100%
rename from dubboctl/templates/go/README.md
rename to dubboctl/pkg/templates/go/README.md
diff --git a/dubboctl/templates/go/common/.gitignore b/dubboctl/pkg/templates/go/common/.gitignore
similarity index 100%
rename from dubboctl/templates/go/common/.gitignore
rename to dubboctl/pkg/templates/go/common/.gitignore
diff --git a/dubboctl/templates/go/common/api/api.pb.go b/dubboctl/pkg/templates/go/common/api/api.pb.go
similarity index 100%
rename from dubboctl/templates/go/common/api/api.pb.go
rename to dubboctl/pkg/templates/go/common/api/api.pb.go
diff --git a/dubboctl/templates/go/common/api/api.proto b/dubboctl/pkg/templates/go/common/api/api.proto
similarity index 100%
rename from dubboctl/templates/go/common/api/api.proto
rename to dubboctl/pkg/templates/go/common/api/api.proto
diff --git a/dubboctl/templates/go/common/api/api_triple.pb.go b/dubboctl/pkg/templates/go/common/api/api_triple.pb.go
similarity index 100%
rename from dubboctl/templates/go/common/api/api_triple.pb.go
rename to dubboctl/pkg/templates/go/common/api/api_triple.pb.go
diff --git a/dubboctl/templates/go/common/cmd/app.go b/dubboctl/pkg/templates/go/common/cmd/app.go
similarity index 100%
rename from dubboctl/templates/go/common/cmd/app.go
rename to dubboctl/pkg/templates/go/common/cmd/app.go
diff --git a/dubboctl/templates/go/common/conf/dubbogo.yaml b/dubboctl/pkg/templates/go/common/conf/dubbogo.yaml
similarity index 100%
rename from dubboctl/templates/go/common/conf/dubbogo.yaml
rename to dubboctl/pkg/templates/go/common/conf/dubbogo.yaml
diff --git a/dubboctl/templates/go/common/go.mod b/dubboctl/pkg/templates/go/common/go.mod
similarity index 100%
rename from dubboctl/templates/go/common/go.mod
rename to dubboctl/pkg/templates/go/common/go.mod
diff --git a/dubboctl/templates/go/common/go.sum b/dubboctl/pkg/templates/go/common/go.sum
similarity index 100%
rename from dubboctl/templates/go/common/go.sum
rename to dubboctl/pkg/templates/go/common/go.sum
diff --git a/dubboctl/templates/go/common/pkg/service/service.go b/dubboctl/pkg/templates/go/common/pkg/service/service.go
similarity index 100%
rename from dubboctl/templates/go/common/pkg/service/service.go
rename to dubboctl/pkg/templates/go/common/pkg/service/service.go
diff --git a/dubboctl/templates/java/README.md b/dubboctl/pkg/templates/java/README.md
similarity index 100%
rename from dubboctl/templates/java/README.md
rename to dubboctl/pkg/templates/java/README.md
diff --git a/dubboctl/templates/java/common/.gitignore b/dubboctl/pkg/templates/java/common/.gitignore
similarity index 100%
rename from dubboctl/templates/java/common/.gitignore
rename to dubboctl/pkg/templates/java/common/.gitignore
diff --git a/dubboctl/templates/java/common/pom.xml b/dubboctl/pkg/templates/java/common/pom.xml
similarity index 100%
rename from dubboctl/templates/java/common/pom.xml
rename to dubboctl/pkg/templates/java/common/pom.xml
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/DemoApplication.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/User.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
diff --git a/dubboctl/templates/java/common/src/main/resources/application.yml b/dubboctl/pkg/templates/java/common/src/main/resources/application.yml
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/application.yml
rename to dubboctl/pkg/templates/java/common/src/main/resources/application.yml
diff --git a/dubboctl/templates/java/common/src/main/resources/log4j.properties b/dubboctl/pkg/templates/java/common/src/main/resources/log4j.properties
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/log4j.properties
rename to dubboctl/pkg/templates/java/common/src/main/resources/log4j.properties
diff --git a/dubboctl/templates/java/common/src/main/resources/static/index.html b/dubboctl/pkg/templates/java/common/src/main/resources/static/index.html
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/static/index.html
rename to dubboctl/pkg/templates/java/common/src/main/resources/static/index.html
diff --git a/dubboctl/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java b/dubboctl/pkg/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
similarity index 100%
rename from dubboctl/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
rename to dubboctl/pkg/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
diff --git a/dubboctl/templates/manifest.yaml b/dubboctl/pkg/templates/manifest.yaml
similarity index 100%
rename from dubboctl/templates/manifest.yaml
rename to dubboctl/pkg/templates/manifest.yaml
diff --git a/go.mod b/go.mod
index 74b5958..8fae009 100644
--- a/go.mod
+++ b/go.mod
@@ -58,6 +58,7 @@
github.com/google/uuid v1.6.0
github.com/google/yamlfmt v0.9.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
+ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/heroku/color v0.0.6
github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69
github.com/jhump/protoreflect v1.16.0
diff --git a/go.sum b/go.sum
index 27760de..1195074 100644
--- a/go.sum
+++ b/go.sum
@@ -2006,3 +2006,7 @@
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
+xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
+xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
\ No newline at end of file
diff --git a/dubboctl/internal/dubbo/client.go b/operator/dubbo/client.go
similarity index 98%
rename from dubboctl/internal/dubbo/client.go
rename to operator/dubbo/client.go
index a9b0d99..0cfafaf 100644
--- a/dubboctl/internal/dubbo/client.go
+++ b/operator/dubbo/client.go
@@ -21,6 +21,8 @@
"bufio"
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"io"
"os"
"path/filepath"
@@ -34,11 +36,6 @@
"gopkg.in/yaml.v3"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
const (
// DefaultTemplate is the default function signature / environmental context
// of the resultant function. All runtimes are expected to have at least
diff --git a/dubboctl/internal/dubbo/client_test.go b/operator/dubbo/client_test.go
similarity index 100%
rename from dubboctl/internal/dubbo/client_test.go
rename to operator/dubbo/client_test.go
diff --git a/dubboctl/internal/dubbo/deploy.tpl b/operator/dubbo/deploy.tpl
similarity index 100%
rename from dubboctl/internal/dubbo/deploy.tpl
rename to operator/dubbo/deploy.tpl
diff --git a/dubboctl/internal/dubbo/deployer.go b/operator/dubbo/deployer.go
similarity index 96%
rename from dubboctl/internal/dubbo/deployer.go
rename to operator/dubbo/deployer.go
index ebd15ee..54d21ba 100644
--- a/dubboctl/internal/dubbo/deployer.go
+++ b/operator/dubbo/deployer.go
@@ -21,14 +21,11 @@
"context"
_ "embed"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
template2 "text/template"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
const (
deployTemplateFile = "deploy.tpl"
)
diff --git a/dubboctl/internal/dubbo/dockerfile.go b/operator/dubbo/dockerfile.go
similarity index 100%
rename from dubboctl/internal/dubbo/dockerfile.go
rename to operator/dubbo/dockerfile.go
diff --git a/dubboctl/internal/dubbo/dubbo.go b/operator/dubbo/dubbo.go
similarity index 100%
rename from dubboctl/internal/dubbo/dubbo.go
rename to operator/dubbo/dubbo.go
diff --git a/dubboctl/internal/dubbo/dubbo_test.go b/operator/dubbo/dubbo_test.go
similarity index 100%
rename from dubboctl/internal/dubbo/dubbo_test.go
rename to operator/dubbo/dubbo_test.go
diff --git a/dubboctl/internal/dubbo/errors.go b/operator/dubbo/errors.go
similarity index 100%
rename from dubboctl/internal/dubbo/errors.go
rename to operator/dubbo/errors.go
diff --git a/dubboctl/internal/dubbo/repositories.go b/operator/dubbo/repositories.go
similarity index 100%
rename from dubboctl/internal/dubbo/repositories.go
rename to operator/dubbo/repositories.go
diff --git a/dubboctl/internal/dubbo/repositories_test.go b/operator/dubbo/repositories_test.go
similarity index 90%
rename from dubboctl/internal/dubbo/repositories_test.go
rename to operator/dubbo/repositories_test.go
index 923ac83..203a00a 100644
--- a/dubboctl/internal/dubbo/repositories_test.go
+++ b/operator/dubbo/repositories_test.go
@@ -18,6 +18,8 @@
package dubbo_test
import (
+ dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
"os"
"path/filepath"
"testing"
@@ -27,11 +29,6 @@
"github.com/google/go-cmp/cmp"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- . "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
-
const RepositoriesTestRepo = "repository.git"
// TestRepositories_List ensures the base case of listing
@@ -40,22 +37,22 @@
root, rm := Mktemp(t)
defer rm()
- client := dubbo.New(dubbo.WithRepositoriesPath(root)) // Explicitly empty
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root)) // Explicitly empty
rr, err := client.Repositories().List()
if err != nil {
t.Fatal(err)
}
// Assert contains only the default repo
- if len(rr) != 1 && rr[0] != dubbo.DefaultRepositoryName {
- t.Fatalf("Expected repository list '[%v]', got %v", dubbo.DefaultRepositoryName, rr)
+ if len(rr) != 1 && rr[0] != dubbo2.DefaultRepositoryName {
+ t.Fatalf("Expected repository list '[%v]', got %v", dubbo2.DefaultRepositoryName, rr)
}
}
// TestRepositories_GetInvalid ensures that attempting to get an invalid repo
// results in error.
func TestRepositories_GetInvalid(t *testing.T) {
- client := dubbo.New(dubbo.WithRepositoriesPath("testdata/repositories"))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath("testdata/repositories"))
// invalid should error
_, err := client.Repositories().Get("invalid")
@@ -66,7 +63,7 @@
// TestRepositories_Get ensures a repository can be accessed by name.
func TestRepositories_Get(t *testing.T) {
- client := dubbo.New(dubbo.WithRepositoriesPath("testdata/repositories"))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath("testdata/repositories"))
// valid should not error
repo, err := client.Repositories().Get("customTemplateRepo")
@@ -87,14 +84,14 @@
root, rm := Mktemp(t)
defer rm()
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Assert initially only the default is included
rr, err := client.Repositories().All()
if err != nil {
t.Fatal(err)
}
- if len(rr) != 1 && rr[0].Name != dubbo.DefaultRepositoryName {
+ if len(rr) != 1 && rr[0].Name != dubbo2.DefaultRepositoryName {
t.Fatalf("Expected initial repo list to be only the default. Got %v", rr)
}
@@ -112,7 +109,7 @@
// Assert it now includes both builtin and extended
if len(repositories) != 2 ||
- repositories[0].Name != dubbo.DefaultRepositoryName ||
+ repositories[0].Name != dubbo2.DefaultRepositoryName ||
repositories[1].Name != "repository" {
t.Fatal("Repositories list does not pass shallow repository membership check")
}
@@ -126,7 +123,7 @@
// Instantiate the client using the current temp directory as the
// repositories' root location.
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Add the repository, explicitly specifying a name. See other tests for
// defaulting from repository names and manifest-defined name.
@@ -162,7 +159,7 @@
root, rm := Mktemp(t)
defer rm()
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
name, err := client.Repositories().Add("", uri)
if err != nil {
@@ -197,7 +194,7 @@
root, rm := Mktemp(t)
defer rm()
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
name, err := client.Repositories().Add("", uri)
if err != nil {
@@ -230,7 +227,7 @@
// Instantiate the client using the current temp directory as the
// repositories' root location.
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Add twice.
name := "example"
@@ -264,7 +261,7 @@
// Instantiate the client using the current temp directory as the
// repositories' root location.
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Add and Rename
if _, err := client.Repositories().Add("foo", uri); err != nil {
@@ -298,7 +295,7 @@
// Instantiate the client using the current temp directory as the
// repositories' root location.
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Add and Remove
name := "example"
@@ -331,7 +328,7 @@
root, rm := Mktemp(t)
defer rm()
- client := dubbo.New(dubbo.WithRepositoriesPath(root))
+ client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
// Add the test repo
_, err := client.Repositories().Add("newrepo", uri)
@@ -359,7 +356,7 @@
// first moving the defaulting path logic from CLI into the client lib.
func TestRepositories_Missing(t *testing.T) {
// Client with no repositories path defined.
- client := dubbo.New()
+ client := dubbo2.New()
// Get all repositories
_, err := client.Repositories().All()
diff --git a/dubboctl/internal/dubbo/repository.go b/operator/dubbo/repository.go
similarity index 99%
rename from dubboctl/internal/dubbo/repository.go
rename to operator/dubbo/repository.go
index c32f282..9080f7f 100644
--- a/dubboctl/internal/dubbo/repository.go
+++ b/operator/dubbo/repository.go
@@ -20,6 +20,7 @@
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
"net/url"
"os"
"path"
@@ -37,10 +38,6 @@
"gopkg.in/yaml.v3"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
const (
repositoryManifest = "manifest.yaml"
templateManifest = "manifest.yaml"
diff --git a/dubboctl/internal/dubbo/template.go b/operator/dubbo/template.go
similarity index 96%
rename from dubboctl/internal/dubbo/template.go
rename to operator/dubbo/template.go
index 3f1ecfd..f331551 100644
--- a/dubboctl/internal/dubbo/template.go
+++ b/operator/dubbo/template.go
@@ -19,13 +19,10 @@
import (
"context"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
"path"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
// Template is a function project template.
// It can be used to instantiate new function project.
type Template interface {
diff --git a/dubboctl/internal/dubbo/templates.go b/operator/dubbo/templates.go
similarity index 97%
rename from dubboctl/internal/dubbo/templates.go
rename to operator/dubbo/templates.go
index 8f4e7bb..5cf5535 100644
--- a/dubboctl/internal/dubbo/templates.go
+++ b/operator/dubbo/templates.go
@@ -19,13 +19,10 @@
import (
"context"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"strings"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// Templates Manager
type Templates struct {
client *Client
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/operator/dubbo/templates_embedded.go
similarity index 89%
rename from dubboctl/internal/dubbo/templates_embedded.go
rename to operator/dubbo/templates_embedded.go
index 987f638..ef2a5f5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/operator/dubbo/templates_embedded.go
@@ -20,11 +20,8 @@
import (
"archive/zip"
"bytes"
-)
-
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/generated"
)
//go:generate go run ../../generated/templates/generate.go
diff --git a/dubboctl/internal/dubbo/templates_test.go b/operator/dubbo/templates_test.go
similarity index 97%
rename from dubboctl/internal/dubbo/templates_test.go
rename to operator/dubbo/templates_test.go
index eef7ded..6264633 100644
--- a/dubboctl/internal/dubbo/templates_test.go
+++ b/operator/dubbo/templates_test.go
@@ -29,8 +29,8 @@
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
- . "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
)
// TestTemplates_List ensures that all templates are listed taking into account
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude b/operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
rename to operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl b/operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
rename to operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go b/operator/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
rename to operator/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js b/operator/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
rename to operator/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js b/operator/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
rename to operator/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/COMMIT_EDITMSG b/operator/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
rename to operator/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/HEAD b/operator/dubbo/testdata/repository-a.git/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/HEAD
rename to operator/dubbo/testdata/repository-a.git/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/config b/operator/dubbo/testdata/repository-a.git/config
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/config
rename to operator/dubbo/testdata/repository-a.git/config
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/description b/operator/dubbo/testdata/repository-a.git/description
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/description
rename to operator/dubbo/testdata/repository-a.git/description
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/hooks/.gitinclude b/operator/dubbo/testdata/repository-a.git/hooks/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/hooks/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/hooks/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/index b/operator/dubbo/testdata/repository-a.git/index
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/index
rename to operator/dubbo/testdata/repository-a.git/index
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/info/exclude b/operator/dubbo/testdata/repository-a.git/info/exclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/info/exclude
rename to operator/dubbo/testdata/repository-a.git/info/exclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/HEAD b/operator/dubbo/testdata/repository-a.git/logs/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/HEAD
rename to operator/dubbo/testdata/repository-a.git/logs/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/heads/main b/operator/dubbo/testdata/repository-a.git/logs/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/heads/main
rename to operator/dubbo/testdata/repository-a.git/logs/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD b/operator/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
rename to operator/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9 b/operator/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
rename to operator/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d b/operator/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
rename to operator/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9 b/operator/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
rename to operator/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2 b/operator/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
rename to operator/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f b/operator/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
rename to operator/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01 b/operator/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
rename to operator/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9 b/operator/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
rename to operator/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b b/operator/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
rename to operator/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed b/operator/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
rename to operator/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/info/.gitinclude b/operator/dubbo/testdata/repository-a.git/objects/info/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/info/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/objects/info/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/.gitinclude b/operator/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx b/operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
rename to operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack b/operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
rename to operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/packed-refs b/operator/dubbo/testdata/repository-a.git/packed-refs
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/packed-refs
rename to operator/dubbo/testdata/repository-a.git/packed-refs
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/.gitinclude b/operator/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/main b/operator/dubbo/testdata/repository-a.git/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/main
rename to operator/dubbo/testdata/repository-a.git/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD b/operator/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
rename to operator/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/tags/.gitinclude b/operator/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/HEAD b/operator/dubbo/testdata/repository.git/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/HEAD
rename to operator/dubbo/testdata/repository.git/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/config b/operator/dubbo/testdata/repository.git/config
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/config
rename to operator/dubbo/testdata/repository.git/config
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/description b/operator/dubbo/testdata/repository.git/description
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/description
rename to operator/dubbo/testdata/repository.git/description
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/applypatch-msg.sample b/operator/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/commit-msg.sample b/operator/dubbo/testdata/repository.git/hooks/commit-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/commit-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/commit-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample b/operator/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
rename to operator/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/post-update.sample b/operator/dubbo/testdata/repository.git/hooks/post-update.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/post-update.sample
rename to operator/dubbo/testdata/repository.git/hooks/post-update.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-applypatch.sample b/operator/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-commit.sample b/operator/dubbo/testdata/repository.git/hooks/pre-commit.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-commit.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-commit.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample b/operator/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-push.sample b/operator/dubbo/testdata/repository.git/hooks/pre-push.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-push.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-push.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-rebase.sample b/operator/dubbo/testdata/repository.git/hooks/pre-rebase.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-rebase.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-rebase.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-receive.sample b/operator/dubbo/testdata/repository.git/hooks/pre-receive.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-receive.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-receive.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample b/operator/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/push-to-checkout.sample b/operator/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
rename to operator/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/update.sample b/operator/dubbo/testdata/repository.git/hooks/update.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/update.sample
rename to operator/dubbo/testdata/repository.git/hooks/update.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/info/exclude b/operator/dubbo/testdata/repository.git/info/exclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/info/exclude
rename to operator/dubbo/testdata/repository.git/info/exclude
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8 b/operator/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
rename to operator/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b b/operator/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
rename to operator/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef b/operator/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
rename to operator/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020 b/operator/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
rename to operator/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c b/operator/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
rename to operator/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69 b/operator/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
rename to operator/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90 b/operator/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
rename to operator/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f b/operator/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
rename to operator/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/operator/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to operator/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/refs/heads/main b/operator/dubbo/testdata/repository.git/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/refs/heads/main
rename to operator/dubbo/testdata/repository.git/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/refs/heads/master b/operator/dubbo/testdata/repository.git/refs/heads/master
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/refs/heads/master
rename to operator/dubbo/testdata/repository.git/refs/heads/master
diff --git a/dubboctl/internal/dubbo/validate.go b/operator/dubbo/validate.go
similarity index 100%
rename from dubboctl/internal/dubbo/validate.go
rename to operator/dubbo/validate.go
diff --git a/dubboctl/internal/http/transport.go b/operator/http/transport.go
similarity index 100%
rename from dubboctl/internal/http/transport.go
rename to operator/http/transport.go
diff --git a/dubboctl/internal/manifest/common.go b/operator/manifest/common.go
similarity index 91%
rename from dubboctl/internal/manifest/common.go
rename to operator/manifest/common.go
index e5326c2..9234cf6 100644
--- a/dubboctl/internal/manifest/common.go
+++ b/operator/manifest/common.go
@@ -17,6 +17,9 @@
import (
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"path"
"strings"
)
@@ -25,12 +28,6 @@
"sigs.k8s.io/yaml"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
func ReadOverlayProfileYaml(profilePath string, profile string) (string, error) {
filePath := path.Join(profilePath, profile+".yaml")
defaultPath := path.Join(profilePath, "default.yaml")
@@ -77,7 +74,7 @@
return "", err
}
// todo:// inspect that this file only contains one CR
- output, err = util.OverlayYAML(output, string(file))
+ output, err = util2.OverlayYAML(output, string(file))
if err != nil {
return "", err
}
@@ -92,7 +89,7 @@
}
for _, setFlag := range setFlags {
key, val := SplitSetFlag(setFlag)
- pathCtx, _, err := GetPathContext(baseMap, util.PathFromString(key), true)
+ pathCtx, _, err := GetPathContext(baseMap, util2.PathFromString(key), true)
if err != nil {
return "", err
}
diff --git a/dubboctl/internal/manifest/common_test.go b/operator/manifest/common_test.go
similarity index 100%
rename from dubboctl/internal/manifest/common_test.go
rename to operator/manifest/common_test.go
diff --git a/dubboctl/internal/manifest/render/render.go b/operator/manifest/render/render.go
similarity index 97%
rename from dubboctl/internal/manifest/render/render.go
rename to operator/manifest/render/render.go
index d6a69f2..5d7673f 100644
--- a/dubboctl/internal/manifest/render/render.go
+++ b/operator/manifest/render/render.go
@@ -18,6 +18,9 @@
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/manifest"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"io/fs"
"net/url"
"os"
@@ -41,12 +44,6 @@
"sigs.k8s.io/yaml"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
const (
YAMLSeparator = "\n---\n"
NotesFileNameSuffix = ".txt"
diff --git a/dubboctl/internal/manifest/render/render_test.go b/operator/manifest/render/render_test.go
similarity index 99%
rename from dubboctl/internal/manifest/render/render_test.go
rename to operator/manifest/render/render_test.go
index 2a6a63d..64fd04c 100644
--- a/dubboctl/internal/manifest/render/render_test.go
+++ b/operator/manifest/render/render_test.go
@@ -16,14 +16,11 @@
package render
import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
"os"
"testing"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-)
-
const (
TestDir string = "testchart"
TestName string = "nginx"
diff --git a/dubboctl/internal/manifest/render/testchart/.helmignore b/operator/manifest/render/testchart/.helmignore
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/.helmignore
rename to operator/manifest/render/testchart/.helmignore
diff --git a/dubboctl/internal/manifest/render/testchart/Chart.yaml b/operator/manifest/render/testchart/Chart.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/Chart.yaml
rename to operator/manifest/render/testchart/Chart.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/NOTES.txt b/operator/manifest/render/testchart/templates/NOTES.txt
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/NOTES.txt
rename to operator/manifest/render/testchart/templates/NOTES.txt
diff --git a/dubboctl/internal/manifest/render/testchart/templates/_helpers.tpl b/operator/manifest/render/testchart/templates/_helpers.tpl
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/_helpers.tpl
rename to operator/manifest/render/testchart/templates/_helpers.tpl
diff --git a/dubboctl/internal/manifest/render/testchart/templates/deployment.yaml b/operator/manifest/render/testchart/templates/deployment.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/deployment.yaml
rename to operator/manifest/render/testchart/templates/deployment.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/hpa.yaml b/operator/manifest/render/testchart/templates/hpa.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/hpa.yaml
rename to operator/manifest/render/testchart/templates/hpa.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/ingress.yaml b/operator/manifest/render/testchart/templates/ingress.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/ingress.yaml
rename to operator/manifest/render/testchart/templates/ingress.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/service.yaml b/operator/manifest/render/testchart/templates/service.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/service.yaml
rename to operator/manifest/render/testchart/templates/service.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/serviceaccount.yaml b/operator/manifest/render/testchart/templates/serviceaccount.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/serviceaccount.yaml
rename to operator/manifest/render/testchart/templates/serviceaccount.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/tests/test-connection.yaml b/operator/manifest/render/testchart/templates/tests/test-connection.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/tests/test-connection.yaml
rename to operator/manifest/render/testchart/templates/tests/test-connection.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/values.yaml b/operator/manifest/render/testchart/values.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/values.yaml
rename to operator/manifest/render/testchart/values.yaml
diff --git a/dubboctl/internal/manifest/tree.go b/operator/manifest/tree.go
similarity index 89%
rename from dubboctl/internal/manifest/tree.go
rename to operator/manifest/tree.go
index f845b27..4446b9d 100644
--- a/dubboctl/internal/manifest/tree.go
+++ b/operator/manifest/tree.go
@@ -33,6 +33,7 @@
"encoding/json"
"errors"
"fmt"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"reflect"
"regexp"
"strconv"
@@ -45,10 +46,6 @@
yaml2 "sigs.k8s.io/yaml"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// PathContext provides a means for traversing a tree towards the root.
type PathContext struct {
// Parent in the Parent of this PathContext.
@@ -78,13 +75,13 @@
// a malformed path.
// It also creates a tree of PathContexts during the traversal so that Parent nodes can be updated if required. This is
// required when (say) appending to a list, where the parent list itself must be updated.
-func GetPathContext(root interface{}, path util.Path, createMissing bool) (*PathContext, bool, error) {
+func GetPathContext(root interface{}, path util2.Path, createMissing bool) (*PathContext, bool, error) {
return getPathContext(&PathContext{Node: root}, path, path, createMissing)
}
// WritePathContext writes the given value to the Node in the given PathContext.
func WritePathContext(nc *PathContext, value interface{}, merge bool) error {
- if !util.IsValueNil(value) {
+ if !util2.IsValueNil(value) {
return setPathContext(nc, value, merge)
}
@@ -94,23 +91,23 @@
switch {
case isSliceOrPtrInterface(nc.Parent.Node):
- if err := util.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil {
+ if err := util2.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil {
return err
}
if isMapOrInterface(nc.Parent.Parent.Node) {
- return util.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node)
+ return util2.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node)
}
// TODO: The case of deleting a list.list.node element is not currently supported.
return fmt.Errorf("cannot delete path: unsupported parent.parent type %T for delete", nc.Parent.Parent.Node)
- case util.IsMap(nc.Parent.Node):
- return util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild)
+ case util2.IsMap(nc.Parent.Node):
+ return util2.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild)
default:
}
return fmt.Errorf("cannot delete path: unsupported parent type %T for delete", nc.Parent.Node)
}
// WriteNode writes value to the tree in root at the given path, creating any required missing internal nodes in path.
-func WriteNode(root interface{}, path util.Path, value interface{}) error {
+func WriteNode(root interface{}, path util2.Path, value interface{}) error {
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
if err != nil {
return err
@@ -119,7 +116,7 @@
}
// MergeNode merges value to the tree in root at the given path, creating any required missing internal nodes in path.
-func MergeNode(root interface{}, path util.Path, value interface{}) error {
+func MergeNode(root interface{}, path util2.Path, value interface{}) error {
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
if err != nil {
return err
@@ -130,7 +127,7 @@
// Find returns the value at path from the given tree, or false if the path does not exist.
// It behaves differently from GetPathContext in that it never creates map entries at the leaf and does not provide
// a way to mutate the parent of the found node.
-func Find(inputTree map[string]interface{}, path util.Path) (interface{}, bool, error) {
+func Find(inputTree map[string]interface{}, path util2.Path) (interface{}, bool, error) {
if len(path) == 0 {
return nil, false, fmt.Errorf("path is empty")
}
@@ -139,7 +136,7 @@
}
// Delete sets value at path of input untyped tree to nil
-func Delete(root map[string]interface{}, path util.Path) (bool, error) {
+func Delete(root map[string]interface{}, path util2.Path) (bool, error) {
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, false)
if err != nil {
return false, err
@@ -149,7 +146,7 @@
// getPathContext is the internal implementation of GetPathContext.
// If createMissing is true, it creates any missing map (but NOT list) path entries in root.
-func getPathContext(nc *PathContext, fullPath, remainPath util.Path, createMissing bool) (*PathContext, bool, error) {
+func getPathContext(nc *PathContext, fullPath, remainPath util2.Path, createMissing bool) (*PathContext, bool, error) {
if len(remainPath) == 0 {
return nc, true, nil
}
@@ -159,7 +156,7 @@
if !createMissing {
return nil, false, fmt.Errorf("node %s is zero", pe)
}
- if util.IsNPathElement(pe) || util.IsKVPathElement(pe) {
+ if util2.IsNPathElement(pe) || util2.IsKVPathElement(pe) {
nc.Node = []interface{}{}
} else {
nc.Node = make(map[string]interface{})
@@ -177,8 +174,8 @@
if lst, ok := ncNode.([]interface{}); ok {
// If the path element has the form [N], a list element is being selected by index. Return the element at index
// N if it exists.
- if util.IsNPathElement(pe) {
- idx, err := util.PathN(pe)
+ if util2.IsNPathElement(pe) {
+ idx, err := util2.PathN(pe)
if err != nil {
return nil, false, fmt.Errorf("path %s, index %s: %s", fullPath, pe, err)
}
@@ -205,7 +202,7 @@
for idx, le := range lst {
// non-leaf list, expect to match item by key:value.
if lm, ok := le.(map[interface{}]interface{}); ok {
- k, v, err := util.PathKV(pe)
+ k, v, err := util2.PathKV(pe)
if err != nil {
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
}
@@ -226,7 +223,7 @@
// repeat of the block above for the case where tree unmarshals to map[string]interface{}. There doesn't
// seem to be a way to merge this case into the above block.
if lm, ok := le.(map[string]interface{}); ok {
- k, v, err := util.PathKV(pe)
+ k, v, err := util2.PathKV(pe)
if err != nil {
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
}
@@ -245,7 +242,7 @@
continue
}
// leaf list, expect path element [V], match based on value V.
- v, err := util.PathV(pe)
+ v, err := util2.PathV(pe)
if err != nil {
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
}
@@ -261,7 +258,7 @@
return nil, false, fmt.Errorf("path %s: element %s not found", fullPath, pe)
}
- if util.IsMap(ncNode) {
+ if util2.IsMap(ncNode) {
var nn interface{}
if m, ok := ncNode.(map[interface{}]interface{}); ok {
nn, ok = m[pe]
@@ -284,7 +281,7 @@
if !ok {
// remainPath == 1 means the patch is creation of a new leaf.
if createMissing || len(remainPath) == 1 {
- nextElementNPath := len(remainPath) > 1 && util.IsNPathElement(remainPath[1])
+ nextElementNPath := len(remainPath) > 1 && util2.IsNPathElement(remainPath[1])
if nextElementNPath {
m[pe] = make([]interface{}, 0)
} else {
@@ -302,7 +299,7 @@
Node: nn,
}
// for slices, use the address so that the slice can be mutated.
- if util.IsSlice(nn) {
+ if util2.IsSlice(nn) {
npc.Node = &nn
}
nc.KeyToChild = pe
@@ -395,12 +392,12 @@
} else {
// For map passed as string type, the root is the new key.
if mapFromString {
- if err := util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil {
+ if err := util2.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil {
return false, err
}
vm := vv.(map[string]interface{})
newKey := getTreeRoot(vm)
- return false, util.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey])
+ return false, util2.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey])
}
parentNode[key] = vv
nc.Node = vv
@@ -419,30 +416,30 @@
// mergeConditional returns a merge of newVal and originalVal if merge is true, otherwise it returns newVal.
func mergeConditional(newVal, originalVal interface{}, merge bool) (interface{}, error) {
- if !merge || util.IsValueNilOrDefault(originalVal) {
+ if !merge || util2.IsValueNilOrDefault(originalVal) {
return newVal, nil
}
newS, err := yaml.Marshal(newVal)
if err != nil {
return nil, err
}
- if util.IsYAMLEmpty(string(newS)) {
+ if util2.IsYAMLEmpty(string(newS)) {
return originalVal, nil
}
originalS, err := yaml.Marshal(originalVal)
if err != nil {
return nil, err
}
- if util.IsYAMLEmpty(string(originalS)) {
+ if util2.IsYAMLEmpty(string(originalS)) {
return newVal, nil
}
- mergedS, err := util.OverlayYAML(string(originalS), string(newS))
+ mergedS, err := util2.OverlayYAML(string(originalS), string(newS))
if err != nil {
return nil, err
}
- if util.IsMap(originalVal) {
+ if util2.IsMap(originalVal) {
// For JSON compatibility
out := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil {
@@ -459,7 +456,7 @@
}
// find returns the value at path from the given tree, or false if the path does not exist.
-func find(treeNode interface{}, path util.Path) (interface{}, bool) {
+func find(treeNode interface{}, path util2.Path) (interface{}, bool) {
if len(path) == 0 || treeNode == nil {
return nil, false
}
diff --git a/dubboctl/internal/manifest/util.go b/operator/manifest/util.go
similarity index 100%
rename from dubboctl/internal/manifest/util.go
rename to operator/manifest/util.go
diff --git a/dubboctl/internal/mock/builder.go b/operator/mock/builder.go
similarity index 93%
rename from dubboctl/internal/mock/builder.go
rename to operator/mock/builder.go
index 1f7bd42..d571b4b 100644
--- a/dubboctl/internal/mock/builder.go
+++ b/operator/mock/builder.go
@@ -17,10 +17,7 @@
import (
"context"
-)
-
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
)
type Builder struct {
diff --git a/dubboctl/internal/mock/client.go b/operator/mock/client.go
similarity index 96%
rename from dubboctl/internal/mock/client.go
rename to operator/mock/client.go
index 9a5627e..d7aa53c 100644
--- a/dubboctl/internal/mock/client.go
+++ b/operator/mock/client.go
@@ -18,7 +18,7 @@
package mock
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
)
type Client struct {
diff --git a/dubboctl/internal/mock/pusher.go b/operator/mock/pusher.go
similarity index 93%
rename from dubboctl/internal/mock/pusher.go
rename to operator/mock/pusher.go
index a383633..ce64082 100644
--- a/dubboctl/internal/mock/pusher.go
+++ b/operator/mock/pusher.go
@@ -17,10 +17,7 @@
import (
"context"
-)
-
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
)
type Pusher struct {
diff --git a/dubboctl/internal/apis/dubbo.apache.org/v1alpha1/types.go b/operator/pkg/apis/dubbo.apache.org/v1alpha1/types.go
similarity index 100%
rename from dubboctl/internal/apis/dubbo.apache.org/v1alpha1/types.go
rename to operator/pkg/apis/dubbo.apache.org/v1alpha1/types.go
diff --git a/dubboctl/internal/builders/builders.go b/operator/pkg/builders/builders.go
similarity index 97%
rename from dubboctl/internal/builders/builders.go
rename to operator/pkg/builders/builders.go
index 4d33cf9..b782990 100644
--- a/dubboctl/internal/builders/builders.go
+++ b/operator/pkg/builders/builders.go
@@ -19,14 +19,11 @@
import (
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
"strconv"
"strings"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
const (
Pack = "pack"
)
diff --git a/dubboctl/internal/builders/builders_test.go b/operator/pkg/builders/builders_test.go
similarity index 94%
rename from dubboctl/internal/builders/builders_test.go
rename to operator/pkg/builders/builders_test.go
index 9313172..00597bf 100644
--- a/dubboctl/internal/builders/builders_test.go
+++ b/operator/pkg/builders/builders_test.go
@@ -19,15 +19,12 @@
import (
"errors"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
"testing"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
// TestImage_Named ensures that a builder image is returned when
// it exists on the function for a given builder, no defaults.
func TestImage_Named(t *testing.T) {
diff --git a/dubboctl/internal/builders/dockerfile/build.go b/operator/pkg/builders/dockerfile/build.go
similarity index 92%
rename from dubboctl/internal/builders/dockerfile/build.go
rename to operator/pkg/builders/dockerfile/build.go
index 5657d0f..46bdbfc 100644
--- a/dubboctl/internal/builders/dockerfile/build.go
+++ b/operator/pkg/builders/dockerfile/build.go
@@ -17,6 +17,8 @@
import (
"context"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"os"
)
@@ -30,11 +32,6 @@
"github.com/moby/term"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
type Builder struct{}
func (b Builder) Build(ctx context.Context, f *dubbo.Dubbo) error {
diff --git a/dubboctl/internal/builders/pack/build.go b/operator/pkg/builders/pack/build.go
similarity index 96%
rename from dubboctl/internal/builders/pack/build.go
rename to operator/pkg/builders/pack/build.go
index 2632ede..d2664e5 100644
--- a/dubboctl/internal/builders/pack/build.go
+++ b/operator/pkg/builders/pack/build.go
@@ -20,6 +20,10 @@
"context"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack/mirror"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"io"
"runtime"
"strings"
@@ -39,13 +43,6 @@
"github.com/heroku/color"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack/mirror"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
// DefaultName when no WithName option is provided to NewBuilder
const DefaultName = builders.Pack
diff --git a/dubboctl/internal/builders/pack/build_test.go b/operator/pkg/builders/pack/build_test.go
similarity index 96%
rename from dubboctl/internal/builders/pack/build_test.go
rename to operator/pkg/builders/pack/build_test.go
index 9622172..c5539a0 100644
--- a/dubboctl/internal/builders/pack/build_test.go
+++ b/operator/pkg/builders/pack/build_test.go
@@ -17,6 +17,8 @@
import (
"context"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/builders"
"reflect"
"testing"
)
@@ -25,11 +27,6 @@
pack "github.com/buildpacks/pack/pkg/client"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
// TestBuild_BuilderImageUntrusted ensures that only known builder images
// are to be considered trusted.
func TestBuild_BuilderImageUntrusted(t *testing.T) {
diff --git a/dubboctl/internal/builders/pack/mirror/error.go b/operator/pkg/builders/pack/mirror/error.go
similarity index 100%
rename from dubboctl/internal/builders/pack/mirror/error.go
rename to operator/pkg/builders/pack/mirror/error.go
diff --git a/dubboctl/internal/builders/pack/mirror/mirror.go b/operator/pkg/builders/pack/mirror/mirror.go
similarity index 100%
rename from dubboctl/internal/builders/pack/mirror/mirror.go
rename to operator/pkg/builders/pack/mirror/mirror.go
diff --git a/dubboctl/internal/docker/creds/credentials.go b/operator/pkg/docker/creds/credentials.go
similarity index 99%
rename from dubboctl/internal/docker/creds/credentials.go
rename to operator/pkg/docker/creds/credentials.go
index 01a44ad..a4d90a0 100644
--- a/dubboctl/internal/docker/creds/credentials.go
+++ b/operator/pkg/docker/creds/credentials.go
@@ -20,6 +20,7 @@
"encoding/json"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"net"
"net/http"
"net/url"
@@ -42,10 +43,6 @@
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
type CredentialsCallback func(registry string) (docker.Credentials, error)
var ErrUnauthorized = errors.New("bad credentials")
diff --git a/dubboctl/internal/docker/creds/credentials_test.go b/operator/pkg/docker/creds/credentials_test.go
similarity index 98%
rename from dubboctl/internal/docker/creds/credentials_test.go
rename to operator/pkg/docker/creds/credentials_test.go
index fb08fde..19b46d9 100644
--- a/dubboctl/internal/docker/creds/credentials_test.go
+++ b/operator/pkg/docker/creds/credentials_test.go
@@ -26,6 +26,9 @@
"encoding/json"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
"io"
"math/big"
"net"
@@ -43,12 +46,6 @@
"github.com/docker/docker-credential-helpers/credentials"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
- . "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
-
func Test_registryEquals(t *testing.T) {
tests := []struct {
name string
diff --git a/dubboctl/internal/docker/docker_client.go b/operator/pkg/docker/docker_client.go
similarity index 94%
rename from dubboctl/internal/docker/docker_client.go
rename to operator/pkg/docker/docker_client.go
index 16345f6..3b9ae3d 100644
--- a/dubboctl/internal/docker/docker_client.go
+++ b/operator/pkg/docker/docker_client.go
@@ -19,6 +19,7 @@
"crypto/tls"
"crypto/x509"
"errors"
+ ssh2 "github.com/apache/dubbo-kubernetes/operator/pkg/ssh"
"io"
"net"
"net/http"
@@ -38,10 +39,6 @@
"github.com/docker/go-connections/tlsconfig"
)
-import (
- fnssh "github.com/apache/dubbo-kubernetes/dubboctl/internal/ssh"
-)
-
var ErrNoDocker = errors.New("docker API not available")
// NewClient creates a new docker client.
@@ -57,7 +54,7 @@
dockerHost := os.Getenv("DOCKER_HOST")
dockerHostSSHIdentity := os.Getenv("DOCKER_HOST_SSH_IDENTITY")
- hostKeyCallback := fnssh.NewHostKeyCbk()
+ hostKeyCallback := ssh2.NewHostKeyCbk()
if dockerHost == "" {
_url, err = url.Parse(defaultHost)
@@ -109,14 +106,14 @@
return
}
- credentialsConfig := fnssh.Config{
+ credentialsConfig := ssh2.Config{
Identity: dockerHostSSHIdentity,
PassPhrase: os.Getenv("DOCKER_HOST_SSH_IDENTITY_PASSPHRASE"),
- PasswordCallback: fnssh.NewPasswordCbk(),
- PassPhraseCallback: fnssh.NewPassPhraseCbk(),
+ PasswordCallback: ssh2.NewPasswordCbk(),
+ PassPhraseCallback: ssh2.NewPassPhraseCbk(),
HostKeyCallback: hostKeyCallback,
}
- contextDialer, dockerHostInRemote, err := fnssh.NewDialContext(_url, credentialsConfig)
+ contextDialer, dockerHostInRemote, err := ssh2.NewDialContext(_url, credentialsConfig)
if err != nil {
return
}
diff --git a/dubboctl/internal/docker/docker_client_ssh_test.go b/operator/pkg/docker/docker_client_ssh_test.go
similarity index 98%
rename from dubboctl/internal/docker/docker_client_ssh_test.go
rename to operator/pkg/docker/docker_client_ssh_test.go
index 4d577a7..0ca7194 100644
--- a/dubboctl/internal/docker/docker_client_ssh_test.go
+++ b/operator/pkg/docker/docker_client_ssh_test.go
@@ -24,6 +24,7 @@
"encoding/binary"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"io"
"net"
"net/http"
@@ -41,10 +42,6 @@
"golang.org/x/crypto/ssh"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
func TestNewDockerClientWithSSH(t *testing.T) {
withCleanHome(t)
diff --git a/dubboctl/internal/docker/docker_client_test.go b/operator/pkg/docker/docker_client_test.go
similarity index 97%
rename from dubboctl/internal/docker/docker_client_test.go
rename to operator/pkg/docker/docker_client_test.go
index 3d4a4a9..b9f30ad 100644
--- a/dubboctl/internal/docker/docker_client_test.go
+++ b/operator/pkg/docker/docker_client_test.go
@@ -18,6 +18,7 @@
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"net"
"net/http"
"path/filepath"
@@ -31,10 +32,6 @@
"github.com/docker/docker/client"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
// Test that we are creating client in accordance
// with the DOCKER_HOST environment variable
func TestNewClient(t *testing.T) {
diff --git a/dubboctl/internal/docker/docker_client_windows_test.go b/operator/pkg/docker/docker_client_windows_test.go
similarity index 95%
rename from dubboctl/internal/docker/docker_client_windows_test.go
rename to operator/pkg/docker/docker_client_windows_test.go
index 7267e39..6370e73 100644
--- a/dubboctl/internal/docker/docker_client_windows_test.go
+++ b/operator/pkg/docker/docker_client_windows_test.go
@@ -18,6 +18,7 @@
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"testing"
"time"
)
@@ -28,10 +29,6 @@
"github.com/docker/docker/client"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
func TestNewClientWinPipe(t *testing.T) {
const testNPipe = "test-npipe"
diff --git a/dubboctl/cmd/prompt/prompt.go b/operator/pkg/docker/prompt/prompt.go
similarity index 95%
rename from dubboctl/cmd/prompt/prompt.go
rename to operator/pkg/docker/prompt/prompt.go
index 91ea6f2..169762f 100644
--- a/dubboctl/cmd/prompt/prompt.go
+++ b/operator/pkg/docker/prompt/prompt.go
@@ -18,6 +18,8 @@
import (
"bufio"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
"io"
"os"
"strings"
@@ -30,11 +32,6 @@
"golang.org/x/term"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
-)
-
func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(registry string) (docker.Credentials, error) {
firstTime := true
return func(registry string) (docker.Credentials, error) {
diff --git a/dubboctl/internal/docker/pusher.go b/operator/pkg/docker/pusher.go
similarity index 98%
rename from dubboctl/internal/docker/pusher.go
rename to operator/pkg/docker/pusher.go
index 1361d3c..fd87084 100644
--- a/dubboctl/internal/docker/pusher.go
+++ b/operator/pkg/docker/pusher.go
@@ -22,6 +22,7 @@
"encoding/json"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
"io"
"net"
"net/http"
@@ -43,10 +44,6 @@
"golang.org/x/term"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
type Opt func(*Pusher)
type Credentials struct {
diff --git a/dubboctl/internal/docker/pusher_test.go b/operator/pkg/docker/pusher_test.go
similarity index 98%
rename from dubboctl/internal/docker/pusher_test.go
rename to operator/pkg/docker/pusher_test.go
index ea83783..ab15d7a 100644
--- a/dubboctl/internal/docker/pusher_test.go
+++ b/operator/pkg/docker/pusher_test.go
@@ -28,6 +28,8 @@
"encoding/json"
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/dubbo"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/docker"
"io"
"log"
"math/big"
@@ -46,11 +48,6 @@
"github.com/google/go-containerregistry/pkg/registry"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
func TestGetRegistry(t *testing.T) {
tests := []struct {
name string
diff --git a/dubboctl/internal/envoy/envoy.go b/operator/pkg/envoy/envoy.go
similarity index 100%
rename from dubboctl/internal/envoy/envoy.go
rename to operator/pkg/envoy/envoy.go
diff --git a/dubboctl/internal/envoy/memory_limit_darwin.go b/operator/pkg/envoy/memory_limit_darwin.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_darwin.go
rename to operator/pkg/envoy/memory_limit_darwin.go
diff --git a/dubboctl/internal/envoy/memory_limit_linux.go b/operator/pkg/envoy/memory_limit_linux.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_linux.go
rename to operator/pkg/envoy/memory_limit_linux.go
diff --git a/dubboctl/internal/envoy/memory_limit_windows.go b/operator/pkg/envoy/memory_limit_windows.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_windows.go
rename to operator/pkg/envoy/memory_limit_windows.go
diff --git a/dubboctl/internal/envoy/remote_bootstrap.go b/operator/pkg/envoy/remote_bootstrap.go
similarity index 100%
rename from dubboctl/internal/envoy/remote_bootstrap.go
rename to operator/pkg/envoy/remote_bootstrap.go
diff --git a/dubboctl/internal/filesystem/filesystem.go b/operator/pkg/filesystem/filesystem.go
similarity index 100%
rename from dubboctl/internal/filesystem/filesystem.go
rename to operator/pkg/filesystem/filesystem.go
diff --git a/dubboctl/generated/templates/generate.go b/operator/pkg/generated/templates/generate.go
similarity index 98%
rename from dubboctl/generated/templates/generate.go
rename to operator/pkg/generated/templates/generate.go
index ec47441..62dff6e 100644
--- a/dubboctl/generated/templates/generate.go
+++ b/operator/pkg/generated/templates/generate.go
@@ -28,7 +28,7 @@
"path/filepath"
)
-// relative path from /dubboctl/internal/dubbo/templates_embedded.go to the `dubboctl` folder
+// relative path from /dubboctl/operator/dubbo/templates_embedded.go to the `dubboctl` folder
const relativePathToRoot = "../../"
var templatesPath = filepath.Join(relativePathToRoot, "templates")
diff --git a/dubboctl/generated/zz_filesystem_generated.go b/operator/pkg/generated/zz_filesystem_generated.go
similarity index 100%
rename from dubboctl/generated/zz_filesystem_generated.go
rename to operator/pkg/generated/zz_filesystem_generated.go
diff --git a/dubboctl/identifier/const.go b/operator/pkg/identifier/const.go
similarity index 100%
rename from dubboctl/identifier/const.go
rename to operator/pkg/identifier/const.go
diff --git a/dubboctl/identifier/path.go b/operator/pkg/identifier/path.go
similarity index 95%
rename from dubboctl/identifier/path.go
rename to operator/pkg/identifier/path.go
index 3de9877..4d373d5 100644
--- a/dubboctl/identifier/path.go
+++ b/operator/pkg/identifier/path.go
@@ -16,11 +16,11 @@
package identifier
import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
"net/url"
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
"github.com/apache/dubbo-kubernetes/manifests"
)
diff --git a/dubboctl/internal/kube/client.go b/operator/pkg/kube/client.go
similarity index 100%
rename from dubboctl/internal/kube/client.go
rename to operator/pkg/kube/client.go
diff --git a/dubboctl/internal/kube/common.go b/operator/pkg/kube/common.go
similarity index 100%
rename from dubboctl/internal/kube/common.go
rename to operator/pkg/kube/common.go
diff --git a/dubboctl/internal/kube/common_test.go b/operator/pkg/kube/common_test.go
similarity index 100%
rename from dubboctl/internal/kube/common_test.go
rename to operator/pkg/kube/common_test.go
diff --git a/dubboctl/internal/kube/component.go b/operator/pkg/kube/component.go
similarity index 94%
rename from dubboctl/internal/kube/component.go
rename to operator/pkg/kube/component.go
index fbfaea3..c4c931d 100644
--- a/dubboctl/internal/kube/component.go
+++ b/operator/pkg/kube/component.go
@@ -16,6 +16,12 @@
package kube
import (
+ manifest2 "github.com/apache/dubbo-kubernetes/operator/manifest"
+ "github.com/apache/dubbo-kubernetes/operator/manifest/render"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+ util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"path"
"strings"
"unicode/utf8"
@@ -29,15 +35,6 @@
"sigs.k8s.io/yaml"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest/render"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
type ComponentName string
const (
@@ -533,7 +530,7 @@
if addOn {
// see /deploy/addons
// values-*.yaml is the base yaml for addon bootstrap
- valsYaml, err = manifest.ReadAndOverlayYamls([]string{
+ valsYaml, err = manifest2.ReadAndOverlayYamls([]string{
path.Join(identifier.Addons, "values-"+string(name)+".yaml"),
})
if err != nil {
@@ -542,12 +539,12 @@
}
// do not use spec != nil cause spec's type is not nil
- if !util.IsValueNil(spec) {
+ if !util2.IsValueNil(spec) {
valsBytes, err = yaml.Marshal(spec)
if err != nil {
return "", err
}
- valsYaml, err = util.OverlayYAML(valsYaml, string(valsBytes))
+ valsYaml, err = util2.OverlayYAML(valsYaml, string(valsBytes))
if err != nil {
return "", err
}
@@ -610,7 +607,7 @@
// setNamespace split base and set namespace.
func setNamespace(base string, namespace string) (string, error) {
var newSegs []string
- segs, err := util.SplitYAML(base)
+ segs, err := util2.SplitYAML(base)
if err != nil {
return "", err
}
@@ -622,11 +619,11 @@
if err := yaml.Unmarshal([]byte(seg), &segMap); err != nil {
return "", err
}
- pathCtx, _, err := manifest.GetPathContext(segMap, util.PathFromString("metadata.namespace"), true)
+ pathCtx, _, err := manifest2.GetPathContext(segMap, util2.PathFromString("metadata.namespace"), true)
if err != nil {
return "", err
}
- if err := manifest.WritePathContext(pathCtx, manifest.ParseValue(namespace), false); err != nil {
+ if err := manifest2.WritePathContext(pathCtx, manifest2.ParseValue(namespace), false); err != nil {
return "", err
}
newSeg, err := yaml.Marshal(segMap)
@@ -635,6 +632,6 @@
}
newSegs = append(newSegs, string(newSeg))
}
- final := util.JoinYAML(newSegs)
+ final := util2.JoinYAML(newSegs)
return final, nil
}
diff --git a/dubboctl/internal/kube/object.go b/operator/pkg/kube/object.go
similarity index 98%
rename from dubboctl/internal/kube/object.go
rename to operator/pkg/kube/object.go
index ffd539e..0c9e5e8 100644
--- a/dubboctl/internal/kube/object.go
+++ b/operator/pkg/kube/object.go
@@ -18,6 +18,7 @@
import (
"bytes"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"sort"
"strings"
)
@@ -27,10 +28,6 @@
"k8s.io/apimachinery/pkg/util/yaml"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
const (
hashPrompt = "Namespace:Kind:Name=>"
)
diff --git a/dubboctl/internal/kube/object_test.go b/operator/pkg/kube/object_test.go
similarity index 100%
rename from dubboctl/internal/kube/object_test.go
rename to operator/pkg/kube/object_test.go
diff --git a/dubboctl/internal/kube/operator.go b/operator/pkg/kube/operator.go
similarity index 97%
rename from dubboctl/internal/kube/operator.go
rename to operator/pkg/kube/operator.go
index d3d9de4..07ab00c 100644
--- a/dubboctl/internal/kube/operator.go
+++ b/operator/pkg/kube/operator.go
@@ -18,11 +18,11 @@
import (
"errors"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
)
import (
- "github.com/apache/dubbo-kubernetes/dubboctl/identifier"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
)
diff --git a/dubboctl/internal/kube/port_foward.go b/operator/pkg/kube/port_foward.go
similarity index 100%
rename from dubboctl/internal/kube/port_foward.go
rename to operator/pkg/kube/port_foward.go
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_manifest.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_manifest.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_manifest.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_manifest.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-create.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-create.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-create.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-create.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-update-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-update-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-update.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-update.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_manifest-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_manifest-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_manifest.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_manifest.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_object-delete.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_object-delete.yaml
diff --git a/dubboctl/internal/kube/testdata/want/admin_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/admin_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/admin_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/admin_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/ctl_client-apply_object-create.yaml b/operator/pkg/kube/testdata/want/ctl_client-apply_object-create.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/ctl_client-apply_object-create.yaml
rename to operator/pkg/kube/testdata/want/ctl_client-apply_object-create.yaml
diff --git a/dubboctl/internal/kube/testdata/want/ctl_client-apply_object-update.yaml b/operator/pkg/kube/testdata/want/ctl_client-apply_object-update.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/ctl_client-apply_object-update.yaml
rename to operator/pkg/kube/testdata/want/ctl_client-apply_object-update.yaml
diff --git a/dubboctl/internal/kube/testdata/want/nacos_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/nacos_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/nacos_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/nacos_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/prometheus_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/skywalking_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/zipkin_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/ssh/ssh_agent_conf.go b/operator/pkg/ssh/ssh_agent_conf.go
similarity index 100%
rename from dubboctl/internal/ssh/ssh_agent_conf.go
rename to operator/pkg/ssh/ssh_agent_conf.go
diff --git a/dubboctl/internal/ssh/ssh_agent_conf_windows.go b/operator/pkg/ssh/ssh_agent_conf_windows.go
similarity index 100%
rename from dubboctl/internal/ssh/ssh_agent_conf_windows.go
rename to operator/pkg/ssh/ssh_agent_conf_windows.go
diff --git a/dubboctl/internal/ssh/ssh_dialer.go b/operator/pkg/ssh/ssh_dialer.go
similarity index 87%
rename from dubboctl/internal/ssh/ssh_dialer.go
rename to operator/pkg/ssh/ssh_dialer.go
index 22bac84..c234450 100644
--- a/dubboctl/internal/ssh/ssh_dialer.go
+++ b/operator/pkg/ssh/ssh_dialer.go
@@ -33,9 +33,9 @@
import (
"github.com/docker/cli/cli/connhelper"
-
+ dockerssh "github.com/docker/cli/cli/connhelper/ssh"
"github.com/docker/docker/pkg/homedir"
-
+ pkgerr "github.com/pkg/errors"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/crypto/ssh/knownhosts"
@@ -239,23 +239,17 @@
}
func stdioDialContext(url *urlPkg.URL, sshClient *ssh.Client, identity string) (DialContextFn, error) {
- session, err := sshClient.NewSession()
+ cmd, err := checkStdioDialer(sshClient)
if err != nil {
return nil, err
}
- defer session.Close()
-
- out, err := session.CombinedOutput("docker system dial-stdio --help")
- if err != nil {
- return nil, fmt.Errorf("cannot use dial-stdio: %w (%q)", err, out)
- }
var opts []string
if identity != "" {
opts = append(opts, "-i", identity)
}
- connHelper, err := connhelper.GetConnectionHelperWithSSHOpts(url.String(), opts)
+ connHelper, err := getConnectionHelper(url.String(), cmd, opts)
if err != nil {
return nil, err
}
@@ -263,6 +257,56 @@
return connHelper.Dialer, nil
}
+// dockerClients is a list of docker clients that support dial-stdio
+var dockerClients = []string{"docker", "podman"}
+
+// checkStdioDialer checks which docker client can be used to dial-stdio
+func checkStdioDialer(sshClient *ssh.Client) (string, error) {
+ var generalError error
+ var session *ssh.Session
+ var err error
+ defer func() {
+ if session != nil {
+ session.Close()
+ }
+ }()
+
+ for _, cmd := range dockerClients {
+ session, err = sshClient.NewSession()
+ if err != nil {
+ return "", err
+ }
+ out, err := session.CombinedOutput(fmt.Sprintf("%s system dial-stdio --help", cmd))
+ session.Close()
+ session = nil
+ if err == nil {
+ return cmd, nil
+ }
+ errors.Join(generalError, fmt.Errorf("checking client %s failed: %w (%q)", cmd, err, string(out)))
+ }
+
+ return "", fmt.Errorf("cannot use dial-stdio with remote docker clients: %v, errors: %w", dockerClients, generalError)
+}
+
+// getConnectionHelper returns Docker-specific connection helper for the given URL and docker client.
+//
+// This is a modified version of the function from pkg "github.com/docker/cli/cli/connhelper"
+func getConnectionHelper(daemonURL string, cmd string, sshFlags []string) (*connhelper.ConnectionHelper, error) {
+ u, err := urlPkg.Parse(daemonURL)
+ if err != nil {
+ return nil, err
+ }
+ switch scheme := u.Scheme; scheme {
+ case "ssh":
+ sp, err := dockerssh.ParseURL(daemonURL)
+ if err != nil {
+ return nil, pkgerr.Wrap(err, "ssh host connection is not valid")
+ }
+ return connhelper.GetCommandConnectionHelper("ssh", append(sshFlags, sp.Args(cmd, "system", "dial-stdio")...)...)
+ }
+ return nil, err
+}
+
// Default key names.
var knownKeyNames = []string{"id_rsa", "id_dsa", "id_ecdsa", "id_ecdsa_sk", "id_ed25519", "id_ed25519_sk"}
diff --git a/dubboctl/internal/ssh/terminal.go b/operator/pkg/ssh/terminal.go
similarity index 100%
rename from dubboctl/internal/ssh/terminal.go
rename to operator/pkg/ssh/terminal.go
diff --git a/dubboctl/internal/testing/testing.go b/operator/pkg/testing/testing.go
similarity index 100%
rename from dubboctl/internal/testing/testing.go
rename to operator/pkg/testing/testing.go
diff --git a/dubboctl/internal/util/config.go b/operator/pkg/util/config.go
similarity index 100%
rename from dubboctl/internal/util/config.go
rename to operator/pkg/util/config.go
diff --git a/dubboctl/internal/util/config_test.go b/operator/pkg/util/config_test.go
similarity index 94%
rename from dubboctl/internal/util/config_test.go
rename to operator/pkg/util/config_test.go
index 9c48864..ab4da55 100644
--- a/dubboctl/internal/util/config_test.go
+++ b/operator/pkg/util/config_test.go
@@ -18,16 +18,13 @@
package util_test
import (
+ . "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+ config "github.com/apache/dubbo-kubernetes/operator/pkg/util"
"os"
"path/filepath"
"testing"
)
-import (
- . "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
- config "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
// TestPath ensures that the Path accessor returns
// XDG_CONFIG_HOME/.config/dubbo
func TestPath(t *testing.T) {
diff --git a/dubboctl/internal/util/env.go b/operator/pkg/util/env.go
similarity index 100%
rename from dubboctl/internal/util/env.go
rename to operator/pkg/util/env.go
diff --git a/dubboctl/internal/util/file.go b/operator/pkg/util/file.go
similarity index 100%
rename from dubboctl/internal/util/file.go
rename to operator/pkg/util/file.go
diff --git a/dubboctl/internal/util/filter.go b/operator/pkg/util/filter.go
similarity index 100%
rename from dubboctl/internal/util/filter.go
rename to operator/pkg/util/filter.go
diff --git a/dubboctl/internal/util/filter_test.go b/operator/pkg/util/filter_test.go
similarity index 100%
rename from dubboctl/internal/util/filter_test.go
rename to operator/pkg/util/filter_test.go
diff --git a/dubboctl/internal/util/golden.go b/operator/pkg/util/golden.go
similarity index 100%
rename from dubboctl/internal/util/golden.go
rename to operator/pkg/util/golden.go
diff --git a/dubboctl/internal/util/golden_test.go b/operator/pkg/util/golden_test.go
similarity index 100%
rename from dubboctl/internal/util/golden_test.go
rename to operator/pkg/util/golden_test.go
diff --git a/dubboctl/internal/util/names.go b/operator/pkg/util/names.go
similarity index 100%
rename from dubboctl/internal/util/names.go
rename to operator/pkg/util/names.go
diff --git a/dubboctl/internal/util/names_test.go b/operator/pkg/util/names_test.go
similarity index 100%
rename from dubboctl/internal/util/names_test.go
rename to operator/pkg/util/names_test.go
diff --git a/dubboctl/internal/util/path.go b/operator/pkg/util/path.go
similarity index 100%
rename from dubboctl/internal/util/path.go
rename to operator/pkg/util/path.go
diff --git a/dubboctl/internal/util/reflect.go b/operator/pkg/util/reflect.go
similarity index 100%
rename from dubboctl/internal/util/reflect.go
rename to operator/pkg/util/reflect.go
diff --git a/dubboctl/internal/util/sortedset.go b/operator/pkg/util/sortedset.go
similarity index 100%
rename from dubboctl/internal/util/sortedset.go
rename to operator/pkg/util/sortedset.go
diff --git a/dubboctl/internal/util/testdata/TestLoad/dubbo/config.yaml b/operator/pkg/util/testdata/TestLoad/dubbo/config.yaml
similarity index 100%
rename from dubboctl/internal/util/testdata/TestLoad/dubbo/config.yaml
rename to operator/pkg/util/testdata/TestLoad/dubbo/config.yaml
diff --git a/dubboctl/internal/util/testdata/dubbo/config.yaml b/operator/pkg/util/testdata/dubbo/config.yaml
similarity index 100%
rename from dubboctl/internal/util/testdata/dubbo/config.yaml
rename to operator/pkg/util/testdata/dubbo/config.yaml
diff --git a/dubboctl/internal/util/yaml.go b/operator/pkg/util/yaml.go
similarity index 100%
rename from dubboctl/internal/util/yaml.go
rename to operator/pkg/util/yaml.go
diff --git a/dubboctl/internal/util/yaml_test.go b/operator/pkg/util/yaml_test.go
similarity index 100%
rename from dubboctl/internal/util/yaml_test.go
rename to operator/pkg/util/yaml_test.go
diff --git a/dubboctl/internal/registry/registry.go b/operator/registry/registry.go
similarity index 100%
rename from dubboctl/internal/registry/registry.go
rename to operator/registry/registry.go
diff --git a/dubboctl/internal/registry/zk/zk.go b/operator/registry/zk/zk.go
similarity index 97%
rename from dubboctl/internal/registry/zk/zk.go
rename to operator/registry/zk/zk.go
index 96338ca..f6d2b7c 100644
--- a/dubboctl/internal/registry/zk/zk.go
+++ b/operator/registry/zk/zk.go
@@ -18,6 +18,7 @@
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/operator/registry"
"net/url"
"strings"
"time"
@@ -27,10 +28,6 @@
"github.com/dubbogo/go-zookeeper/zk"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/registry"
-)
-
type zkRegistry struct {
client *zk.Conn
}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/api-server/authn/authenticator.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/api-server/authn/authenticator.go
index 987f638..69bb768 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/api-server/authn/authenticator.go
@@ -15,25 +15,10 @@
* limitations under the License.
*/
-package dubbo
+package authn
import (
- "archive/zip"
- "bytes"
+ "github.com/emicklei/go-restful/v3"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
-}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+type Authenticator = restful.FilterFunction
diff --git a/pkg/api-server/authn/localhost.go b/pkg/api-server/authn/localhost.go
new file mode 100644
index 0000000..7532f24
--- /dev/null
+++ b/pkg/api-server/authn/localhost.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package authn
+
+import (
+ "net"
+
+ "github.com/emicklei/go-restful/v3"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+var log = core.Log.WithName("api-server").WithName("authn")
+
+func LocalhostAuthenticator(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+ host, _, err := net.SplitHostPort(request.Request.RemoteAddr)
+ if err != nil {
+ //rest_errors.HandleError(request.Request.Context(), response, err, "Could not parse Remote Address from the Request")
+ panic(err)
+ return
+ }
+ if host == "127.0.0.1" || host == "::1" {
+ log.V(1).Info("authenticated as admin because requests originates from the same machine")
+ request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
+ }
+ chain.ProcessFilter(request, response)
+}
diff --git a/pkg/api-server/authn/skip.go b/pkg/api-server/authn/skip.go
new file mode 100644
index 0000000..c187a00
--- /dev/null
+++ b/pkg/api-server/authn/skip.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package authn
+
+import (
+ "strings"
+
+ "github.com/emicklei/go-restful/v3"
+)
+
+const (
+ MetadataAuthKey = "auth"
+ MetadataAuthSkip = "skip"
+)
+
+// UnauthorizedPathPrefixes is another way to add path prefixes as unauthorized endpoint.
+// Prefer MetadataAuthKey, use UnauthorizedPathPrefixes if needed.
+var UnauthorizedPathPrefixes = map[string]struct{}{
+ "/gui": {},
+}
+
+func SkipAuth(request *restful.Request) bool {
+ if route := request.SelectedRoute(); route != nil {
+ if route.Metadata()[MetadataAuthKey] == MetadataAuthSkip {
+ return true
+ }
+ }
+ for prefix := range UnauthorizedPathPrefixes {
+ if strings.HasPrefix(request.Request.RequestURI, prefix) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/pkg/api-server/customization/api_manager.go b/pkg/api-server/customization/api_manager.go
new file mode 100644
index 0000000..69d4f4c
--- /dev/null
+++ b/pkg/api-server/customization/api_manager.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package customization
+
+import (
+ "github.com/emicklei/go-restful/v3"
+)
+
+type APIInstaller interface {
+ Install(container *restful.Container)
+}
+
+type APIManager interface {
+ APIInstaller
+ Add(ws *restful.WebService)
+ AddFilter(filter restful.FilterFunction)
+}
+
+type APIList struct {
+ list []*restful.WebService
+ filters []restful.FilterFunction
+}
+
+func NewAPIList() *APIList {
+ return &APIList{
+ list: []*restful.WebService{},
+ filters: []restful.FilterFunction{},
+ }
+}
+
+func (c *APIList) Add(ws *restful.WebService) {
+ c.list = append(c.list, ws)
+}
+
+func (c *APIList) AddFilter(f restful.FilterFunction) {
+ c.filters = append(c.filters, f)
+}
+
+func (c *APIList) Install(container *restful.Container) {
+ for _, ws := range c.list {
+ container.Add(ws)
+ }
+
+ for _, f := range c.filters {
+ container.Filter(f)
+ }
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/api-server/types/errors.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/api-server/types/errors.go
index 987f638..577e6cf 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/api-server/types/errors.go
@@ -15,25 +15,27 @@
* limitations under the License.
*/
-package dubbo
+package types
import (
- "archive/zip"
- "bytes"
+ "fmt"
+ "reflect"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type InvalidPageSizeError struct {
+ Reason string
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (a *InvalidPageSizeError) Error() string {
+ return a.Reason
+}
+
+func (a *InvalidPageSizeError) Is(err error) bool {
+ return reflect.TypeOf(a) == reflect.TypeOf(err)
+}
+
+func NewMaxPageSizeExceeded(pageSize, limit int) error {
+ return &InvalidPageSizeError{Reason: fmt.Sprintf("invalid page size of %d. Maximum page size is %d", pageSize, limit)}
+}
+
+var InvalidPageSize = &InvalidPageSizeError{Reason: "invalid format"}
diff --git a/pkg/config/access/config.go b/pkg/config/access/config.go
new file mode 100644
index 0000000..e476466
--- /dev/null
+++ b/pkg/config/access/config.go
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/config"
+)
+
+const StaticType = "static"
+
+func DefaultAccessConfig() AccessConfig {
+ return AccessConfig{
+ Type: StaticType,
+ Static: StaticAccessConfig{
+ AdminResources: AdminResourcesStaticAccessConfig{
+ Users: []string{"mesh-system:admin"},
+ Groups: []string{"mesh-system:admin"},
+ },
+ GenerateDPToken: GenerateDPTokenStaticAccessConfig{
+ Users: []string{"mesh-system:admin"},
+ Groups: []string{"mesh-system:admin"},
+ },
+ GenerateUserToken: GenerateUserTokenStaticAccessConfig{
+ Users: []string{"mesh-system:admin"},
+ Groups: []string{"mesh-system:admin"},
+ },
+ GenerateZoneToken: GenerateZoneTokenStaticAccessConfig{
+ Users: []string{"mesh-system:admin"},
+ Groups: []string{"mesh-system:admin"},
+ },
+ ViewConfigDump: ViewConfigDumpStaticAccessConfig{
+ Users: []string{},
+ Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+ },
+ ViewStats: ViewStatsStaticAccessConfig{
+ Users: []string{},
+ Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+ },
+ ViewClusters: ViewClustersStaticAccessConfig{
+ Users: []string{},
+ Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+ },
+ ControlPlaneMetadata: ControlPlaneMetadataStaticAccessConfig{
+ Users: []string{},
+ Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+ },
+ },
+ }
+}
+
+// AccessConfig defines a configuration for access control
+type AccessConfig struct {
+ config.BaseConfig
+
+ // Type of the access strategy (available values: "static")
+ Type string `json:"type" envconfig:"DUBBO_ACCESS_TYPE"`
+ // Configuration of static access strategy
+ Static StaticAccessConfig `json:"static"`
+}
+
+func (r AccessConfig) Validate() error {
+ if r.Type == "" {
+ return errors.New("Type has to be defined")
+ }
+ return nil
+}
+
+var _ config.Config = &AccessConfig{}
+
+// StaticAccessConfig a static access strategy configuration
+type StaticAccessConfig struct {
+ // AdminResources defines an access to admin resources (Secret/GlobalSecret)
+ AdminResources AdminResourcesStaticAccessConfig `json:"adminResources"`
+ // GenerateDPToken defines an access to generating dataplane token
+ GenerateDPToken GenerateDPTokenStaticAccessConfig `json:"generateDpToken"`
+ // GenerateUserToken defines an access to generating user token
+ GenerateUserToken GenerateUserTokenStaticAccessConfig `json:"generateUserToken"`
+ // GenerateZoneToken defines an access to generating zone token
+ GenerateZoneToken GenerateZoneTokenStaticAccessConfig `json:"generateZoneToken"`
+ // ViewConfigDump defines an access to getting envoy config dump
+ ViewConfigDump ViewConfigDumpStaticAccessConfig `json:"viewConfigDump"`
+ // ViewStats defines an access to getting envoy stats
+ ViewStats ViewStatsStaticAccessConfig `json:"viewStats"`
+ // ViewClusters defines an access to getting envoy clusters
+ ViewClusters ViewClustersStaticAccessConfig `json:"viewClusters"`
+ // ControlPlaneMetadata defines an access for control-plane metadata (for example config)
+ ControlPlaneMetadata ControlPlaneMetadataStaticAccessConfig `json:"controlPlaneMetadata"`
+}
+
+type AdminResourcesStaticAccessConfig struct {
+ // List of users that are allowed to access admin resources
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_ADMIN_RESOURCES_USERS"`
+ // List of groups that are allowed to access admin resources
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_ADMIN_RESOURCES_GROUPS"`
+}
+
+type GenerateDPTokenStaticAccessConfig struct {
+ // List of users that are allowed to generate dataplane token
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_DP_TOKEN_USERS"`
+ // List of groups that are allowed to generate dataplane token
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_DP_TOKEN_GROUPS"`
+}
+
+type GenerateUserTokenStaticAccessConfig struct {
+ // List of users that are allowed to generate user token
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_USER_TOKEN_USERS"`
+ // List of groups that are allowed to generate user token
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_USER_TOKEN_GROUPS"`
+}
+
+type GenerateZoneTokenStaticAccessConfig struct {
+ // List of users that are allowed to generate zone token
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_ZONE_TOKEN_USERS"`
+ // List of groups that are allowed to generate zone token
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_ZONE_TOKEN_GROUPS"`
+}
+
+type ViewConfigDumpStaticAccessConfig struct {
+ // List of users that are allowed to get envoy config dump
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GET_CONFIG_DUMP_USERS"`
+ // List of groups that are allowed to get envoy config dump
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GET_CONFIG_DUMP_GROUPS"`
+}
+
+type ViewStatsStaticAccessConfig struct {
+ // List of users that are allowed to get envoy config stats
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_VIEW_STATS_USERS"`
+ // List of groups that are allowed to get envoy config stats
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_VIEW_STATS_GROUPS"`
+}
+
+type ViewClustersStaticAccessConfig struct {
+ // List of users that are allowed to get envoy config clusters
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_VIEW_CLUSTERS_USERS"`
+ // List of groups that are allowed to get envoy config clusters
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_VIEW_CLUSTERS_GROUPS"`
+}
+
+type ControlPlaneMetadataStaticAccessConfig struct {
+ // List of users that are allowed to access control-plane metadata
+ Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_CONTROL_PLANE_METADATA_USERS"`
+ // List of groups that are allowed to access control-plane metadata
+ Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_CONTROL_PLANE_METADATA_GROUPS"`
+}
diff --git a/pkg/config/api-server/config.go b/pkg/config/api-server/config.go
new file mode 100644
index 0000000..c12ee73
--- /dev/null
+++ b/pkg/config/api-server/config.go
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api_server
+
+import (
+ "net/url"
+
+ "github.com/pkg/errors"
+ "go.uber.org/multierr"
+
+ "github.com/apache/dubbo-kubernetes/pkg/config"
+ config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+var _ config.Config = &ApiServerConfig{}
+
+// ApiServerConfig defines API Server configuration
+type ApiServerConfig struct {
+ config.BaseConfig
+
+ // If true, then API Server will operate in read only mode (serving GET requests)
+ ReadOnly bool `json:"readOnly" envconfig:"dubbo_api_server_read_only"`
+ // Allowed domains for Cross-Origin Resource Sharing. The value can be either domain or regexp
+ CorsAllowedDomains []string `json:"corsAllowedDomains" envconfig:"dubbo_api_server_cors_allowed_domains"`
+ // HTTP configuration of the API Server
+ HTTP ApiServerHTTPConfig `json:"http"`
+ // HTTPS configuration of the API Server
+ HTTPS ApiServerHTTPSConfig `json:"https"`
+ // Authentication configuration for administrative endpoints like Dataplane Token or managing Secrets
+ Auth ApiServerAuth `json:"auth"`
+ // Authentication configuration for API Server
+ Authn ApiServerAuthn `json:"authn"`
+ // BasePath the path to serve the API from
+ BasePath string `json:"basePath" envconfig:"dubbo_api_server_base_path"`
+ // RootUrl can be used if you use a reverse proxy
+ RootUrl string `json:"rootUrl" envconfig:"dubbo_api_server_root_url"`
+ // GUI configuration specific to the GUI
+ GUI ApiServerGUI `json:"gui,omitempty"`
+}
+
+type ApiServerGUI struct {
+ // Enabled whether to serve to gui (if mode=zone this has no effect)
+ Enabled bool `json:"enabled" envconfig:"dubbo_api_server_gui_enabled"`
+ // RootUrl can be used if you set a reverse proxy or want to serve the gui from a different path
+ RootUrl string `json:"rootUrl" envconfig:"dubbo_api_server_gui_root_url"`
+ // BasePath the path to serve the GUI from
+ BasePath string `json:"basePath" envconfig:"dubbo_api_server_gui_base_path"`
+}
+
+func (a *ApiServerGUI) Validate() error {
+ var errs error
+ if a.RootUrl != "" {
+ _, err := url.Parse(a.RootUrl)
+ if err != nil {
+ errs = multierr.Append(errs, errors.New("RootUrl is not a valid url"))
+ }
+ }
+ if a.BasePath != "" {
+ _, err := url.Parse(a.BasePath)
+ if err != nil {
+ errs = multierr.Append(errs, errors.New("BaseGuiPath is not a valid url"))
+ }
+ }
+ return errs
+}
+
+// ApiServerHTTPConfig defines API Server HTTP configuration
+type ApiServerHTTPConfig struct {
+ // If true then API Server will be served on HTTP
+ Enabled bool `json:"enabled" envconfig:"dubbo_api_server_http_enabled"`
+ // Network interface on which HTTP API Server will be exposed
+ Interface string `json:"interface" envconfig:"dubbo_api_server_http_interface"`
+ // Port of the HTTP API Server
+ Port uint32 `json:"port" envconfig:"dubbo_api_server_http_port"`
+}
+
+func (a *ApiServerHTTPConfig) Validate() error {
+ var errs error
+ if a.Interface == "" {
+ errs = multierr.Append(errs, errors.New("Interface cannot be empty"))
+ }
+ if a.Port > 65535 {
+ errs = multierr.Append(errs, errors.New("Port must be in range [0, 65535]"))
+ }
+ return errs
+}
+
+// ApiServerHTTPSConfig defines API Server HTTPS configuration
+type ApiServerHTTPSConfig struct {
+ // If true then API Server will be served on HTTPS
+ Enabled bool `json:"enabled" envconfig:"dubbo_api_server_https_enabled"`
+ // Network interface on which HTTPS API Server will be exposed
+ Interface string `json:"interface" envconfig:"dubbo_api_server_https_interface"`
+ // Port of the HTTPS API Server
+ Port uint32 `json:"port" envconfig:"dubbo_api_server_https_port"`
+ // Path to TLS certificate file. Autoconfigured from dubbo_GENERAL_TLS_CERT_FILE if empty
+ TlsCertFile string `json:"tlsCertFile" envconfig:"dubbo_api_server_https_tls_cert_file"`
+ // Path to TLS key file. Autoconfigured from dubbo_GENERAL_TLS_KEY_FILE if empty
+ TlsKeyFile string `json:"tlsKeyFile" envconfig:"dubbo_api_server_https_tls_key_file"`
+ // TlsMinVersion defines the minimum TLS version to be used
+ TlsMinVersion string `json:"tlsMinVersion" envconfig:"dubbo_api_server_https_tls_min_version"`
+ // TlsMaxVersion defines the maximum TLS version to be used
+ TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"dubbo_api_server_https_tls_max_version"`
+ // TlsCipherSuites defines the list of ciphers to use
+ TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"dubbo_api_server_https_tls_cipher_suites"`
+ // If true, then HTTPS connection will require client cert.
+ RequireClientCert bool `json:"requireClientCert" envconfig:"dubbo_api_server_https_require_client_cert"`
+ // Path to the CA certificate which is used to sign client certificates. It is used only for verifying client certificates.
+ TlsCaFile string `json:"tlsCaFile" envconfig:"dubbo_api_server_https_tls_ca_file"`
+}
+
+func (a *ApiServerHTTPSConfig) Validate() error {
+ var errs error
+ if a.Interface == "" {
+ errs = multierr.Append(errs, errors.New(".Interface cannot be empty"))
+ }
+ if a.Port > 65535 {
+ return errors.New("Port must be in range [0, 65535]")
+ }
+ if (a.TlsKeyFile == "" && a.TlsCertFile != "") || (a.TlsKeyFile != "" && a.TlsCertFile == "") {
+ errs = multierr.Append(errs, errors.New("Both TlsCertFile and TlsKeyFile has to be specified"))
+ }
+ if _, err := config_types.TLSVersion(a.TlsMinVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMinVersion"+err.Error()))
+ }
+ if _, err := config_types.TLSVersion(a.TlsMaxVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMaxVersion"+err.Error()))
+ }
+ if _, err := config_types.TLSCiphers(a.TlsCipherSuites); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsCipherSuites"+err.Error()))
+ }
+ return errs
+}
+
+// ApiServerAuth defines API Server authentication configuration
+type ApiServerAuth struct {
+ // Directory of authorized client certificates (only valid in HTTPS)
+ ClientCertsDir string `json:"clientCertsDir" envconfig:"dubbo_api_server_auth_client_certs_dir"`
+}
+
+// ApiServerAuthn defines Api Server Authentication configuration
+type ApiServerAuthn struct {
+ // Type of authentication mechanism (available values: "clientCerts", "tokens")
+ Type string `json:"type" envconfig:"dubbo_api_server_authn_type"`
+ // Localhost is authenticated as a user admin of group admin
+ LocalhostIsAdmin bool `json:"localhostIsAdmin" envconfig:"dubbo_api_server_authn_localhost_is_admin"`
+ // Configuration for tokens authentication
+ Tokens ApiServerAuthnTokens `json:"tokens"`
+}
+
+func (a ApiServerAuthn) Validate() error {
+ if a.Type == "tokens" {
+ if err := a.Tokens.Validate(); err != nil {
+ return errors.Wrap(err, ".Tokens is not valid")
+ }
+ }
+ return nil
+}
+
+type ApiServerAuthnTokens struct {
+ // If true then User Token with name admin and group admin will be created and placed as admin-user-token dubbo Global Secret
+ BootstrapAdminToken bool `json:"bootstrapAdminToken" envconfig:"dubbo_api_server_authn_tokens_bootstrap_admin_token"`
+ // If true the control plane token issuer is enabled. It's recommended to set it to false when all the tokens are issued offline.
+ EnableIssuer bool `json:"enableIssuer" envconfig:"dubbo_api_server_authn_tokens_enable_issuer"`
+ // Token validator configuration
+ Validator TokensValidator `json:"validator"`
+}
+
+func (a ApiServerAuthnTokens) Validate() error {
+ if err := a.Validator.Validate(); err != nil {
+ return errors.Wrap(err, ".Validator is not valid")
+ }
+ return nil
+}
+
+var _ config.Config = TokensValidator{}
+
+type TokensValidator struct {
+ config.BaseConfig
+
+ // If true then dubbo secrets with prefix "user-token-signing-key" are considered as signing keys.
+ UseSecrets bool `json:"useSecrets" envconfig:"dubbo_api_server_authn_tokens_validator_use_secrets"`
+ // List of public keys used to validate the token.
+ PublicKeys []config_types.PublicKey `json:"publicKeys"`
+}
+
+func (t TokensValidator) Validate() error {
+ for i, key := range t.PublicKeys {
+ if err := key.Validate(); err != nil {
+ return errors.Wrapf(err, ".PublicKeys[%d] is not valid", i)
+ }
+ }
+ return nil
+}
+
+func (a *ApiServerConfig) Validate() error {
+ var errs error
+ if err := a.HTTP.Validate(); err != nil {
+ errs = multierr.Append(err, errors.Wrap(err, ".HTTP not valid"))
+ }
+ if err := a.HTTPS.Validate(); err != nil {
+ errs = multierr.Append(err, errors.Wrap(err, ".HTTPS not valid"))
+ }
+ if err := a.GUI.Validate(); err != nil {
+ errs = multierr.Append(err, errors.Wrap(err, ".GUI not valid"))
+ }
+ if a.RootUrl != "" {
+ if _, err := url.Parse(a.RootUrl); err != nil {
+ errs = multierr.Append(err, errors.New("RootUrl is not a valid URL"))
+ }
+ }
+ if a.BasePath != "" {
+ _, err := url.Parse(a.BasePath)
+ if err != nil {
+ errs = multierr.Append(errs, errors.New("BaseGuiPath is not a valid url"))
+ }
+ }
+ if err := a.Authn.Validate(); err != nil {
+ errs = multierr.Append(err, errors.Wrap(err, ".Authn is not valid"))
+ }
+ return errs
+}
+
+func DefaultApiServerConfig() *ApiServerConfig {
+ return &ApiServerConfig{
+ ReadOnly: false,
+ CorsAllowedDomains: []string{".*"},
+ BasePath: "/",
+ HTTP: ApiServerHTTPConfig{
+ Enabled: true,
+ Interface: "0.0.0.0",
+ Port: 5681,
+ },
+ HTTPS: ApiServerHTTPSConfig{
+ Enabled: true,
+ Interface: "0.0.0.0",
+ Port: 5682,
+ TlsCertFile: "", // autoconfigured
+ TlsKeyFile: "", // autoconfigured
+ TlsMinVersion: "TLSv1_2",
+ TlsCipherSuites: []string{},
+ },
+ Auth: ApiServerAuth{
+ ClientCertsDir: "",
+ },
+ Authn: ApiServerAuthn{
+ Type: "tokens",
+ LocalhostIsAdmin: true,
+ Tokens: ApiServerAuthnTokens{
+ BootstrapAdminToken: true,
+ EnableIssuer: true,
+ Validator: TokensValidator{
+ UseSecrets: true,
+ PublicKeys: []config_types.PublicKey{},
+ },
+ },
+ },
+ GUI: ApiServerGUI{
+ Enabled: true,
+ BasePath: "/gui",
+ },
+ }
+}
diff --git a/pkg/config/app/dubbo-cp/config.go b/pkg/config/app/dubbo-cp/config.go
index ab9a1ef..82efdd1 100644
--- a/pkg/config/app/dubbo-cp/config.go
+++ b/pkg/config/app/dubbo-cp/config.go
@@ -19,26 +19,27 @@
import (
"time"
-)
-import (
- "github.com/pkg/errors"
-
- "go.uber.org/multierr"
-)
-
-import (
"github.com/apache/dubbo-kubernetes/pkg/config"
+ "github.com/apache/dubbo-kubernetes/pkg/config/access"
"github.com/apache/dubbo-kubernetes/pkg/config/admin"
+ "github.com/apache/dubbo-kubernetes/pkg/config/intercp"
+ "github.com/asaskevich/govalidator"
+ "github.com/pkg/errors"
+ "go.uber.org/multierr"
+
+ api_server "github.com/apache/dubbo-kubernetes/pkg/config/api-server"
"github.com/apache/dubbo-kubernetes/pkg/config/bufman"
"github.com/apache/dubbo-kubernetes/pkg/config/core"
"github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
"github.com/apache/dubbo-kubernetes/pkg/config/diagnostics"
+
dp_server "github.com/apache/dubbo-kubernetes/pkg/config/dp-server"
"github.com/apache/dubbo-kubernetes/pkg/config/dubbo"
"github.com/apache/dubbo-kubernetes/pkg/config/eventbus"
"github.com/apache/dubbo-kubernetes/pkg/config/multizone"
"github.com/apache/dubbo-kubernetes/pkg/config/plugins/runtime"
+
config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
"github.com/apache/dubbo-kubernetes/pkg/config/xds"
"github.com/apache/dubbo-kubernetes/pkg/config/xds/bootstrap"
@@ -55,6 +56,116 @@
SkipMeshCreation bool `json:"skipMeshCreation" envconfig:"DUBBO_DEFAULTS_SKIP_MESH_CREATION"`
}
+type DataplaneMetrics struct {
+ config.BaseConfig
+
+ SubscriptionLimit int `json:"subscriptionLimit" envconfig:"Dubbo_metrics_dataplane_subscription_limit"`
+ IdleTimeout config_types.Duration `json:"idleTimeout" envconfig:"Dubbo_metrics_dataplane_idle_timeout"`
+}
+
+type ZoneMetrics struct {
+ config.BaseConfig
+
+ SubscriptionLimit int `json:"subscriptionLimit" envconfig:"Dubbo_metrics_zone_subscription_limit"`
+ IdleTimeout config_types.Duration `json:"idleTimeout" envconfig:"Dubbo_metrics_zone_idle_timeout"`
+ // CompactFinishedSubscriptions compacts finished metrics (do not store config and details of KDS exchange).
+ CompactFinishedSubscriptions bool `json:"compactFinishedSubscriptions" envconfig:"Dubbo_metrics_zone_compact_finished_subscriptions"`
+}
+
+type MeshMetrics struct {
+ config.BaseConfig
+
+ // Deprecated: use MinResyncInterval instead
+ MinResyncTimeout config_types.Duration `json:"minResyncTimeout" envconfig:"Dubbo_metrics_mesh_min_resync_timeout"`
+ // Deprecated: use FullResyncInterval instead
+ MaxResyncTimeout config_types.Duration `json:"maxResyncTimeout" envconfig:"Dubbo_metrics_mesh_max_resync_timeout"`
+ // BufferSize the size of the buffer between event creation and processing
+ BufferSize int `json:"bufferSize" envconfig:"Dubbo_metrics_mesh_buffer_size"`
+ // MinResyncInterval the minimum time between 2 refresh of insights.
+ MinResyncInterval config_types.Duration `json:"minResyncInterval" envconfig:"Dubbo_metrics_mesh_min_resync_interval"`
+ // FullResyncInterval time between triggering a full refresh of all the insights
+ FullResyncInterval config_types.Duration `json:"fullResyncInterval" envconfig:"Dubbo_metrics_mesh_full_resync_interval"`
+ // EventProcessors is a number of workers that process metrics events.
+ EventProcessors int `json:"eventProcessors" envconfig:"Dubbo_metrics_mesh_event_processors"`
+}
+
+type Metrics struct {
+ config.BaseConfig
+
+ Dataplane *DataplaneMetrics `json:"dataplane"`
+ Zone *ZoneMetrics `json:"zone"`
+ Mesh *MeshMetrics `json:"mesh"`
+ ControlPlane *ControlPlaneMetrics `json:"controlPlane"`
+}
+
+type ControlPlaneMetrics struct {
+ // ReportResourcesCount if true will report metrics with the count of resources.
+ // Default: true
+ ReportResourcesCount bool `json:"reportResourcesCount" envconfig:"Dubbo_metrics_control_plane_report_resources_count"`
+}
+
+type InterCpConfig struct {
+ // Catalog configuration. Catalog keeps a record of all live CP instances in the zone.
+ Catalog CatalogConfig `json:"catalog"`
+ // Intercommunication CP server configuration
+ Server InterCpServerConfig `json:"server"`
+}
+
+func (i *InterCpConfig) Validate() error {
+ if err := i.Server.Validate(); err != nil {
+ return errors.Wrap(err, ".Server validation failed")
+ }
+ if err := i.Catalog.Validate(); err != nil {
+ return errors.Wrap(err, ".Catalog validation failed")
+ }
+ return nil
+}
+
+type CatalogConfig struct {
+ // InstanceAddress indicates an address on which other control planes can communicate with this CP
+ // If empty then it's autoconfigured by taking the first IP of the nonloopback network interface.
+ InstanceAddress string `json:"instanceAddress" envconfig:"Dubbo_inter_cp_catalog_instance_address"`
+ // Interval on which CP will send heartbeat to a leader.
+ HeartbeatInterval config_types.Duration `json:"heartbeatInterval" envconfig:"Dubbo_inter_cp_catalog_heartbeat_interval"`
+ // Interval on which CP will write all instances to a catalog.
+ WriterInterval config_types.Duration `json:"writerInterval" envconfig:"Dubbo_inter_cp_catalog_writer_interval"`
+}
+
+func (i *CatalogConfig) Validate() error {
+ if i.InstanceAddress != "" && !govalidator.IsDNSName(i.InstanceAddress) && !govalidator.IsIP(i.InstanceAddress) {
+ return errors.New(".InstanceAddress has to be valid IP or DNS address")
+ }
+ return nil
+}
+
+type InterCpServerConfig struct {
+ // Port on which Intercommunication CP server will listen
+ Port uint16 `json:"port" envconfig:"Dubbo_inter_cp_server_port"`
+ // TlsMinVersion defines the minimum TLS version to be used
+ TlsMinVersion string `json:"tlsMinVersion" envconfig:"Dubbo_inter_cp_server_tls_min_version"`
+ // TlsMaxVersion defines the maximum TLS version to be used
+ TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"Dubbo_inter_cp_server_tls_max_version"`
+ // TlsCipherSuites defines the list of ciphers to use
+ TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"Dubbo_inter_cp_server_tls_cipher_suites"`
+}
+
+func (i *InterCpServerConfig) Validate() error {
+ var errs error
+ if i.Port == 0 {
+ errs = multierr.Append(errs, errors.New(".Port cannot be zero"))
+ }
+ if _, err := config_types.TLSVersion(i.TlsMinVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMinVersion "+err.Error()))
+ }
+ if _, err := config_types.TLSVersion(i.TlsMaxVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMaxVersion "+err.Error()))
+ }
+ if _, err := config_types.TLSCiphers(i.TlsCipherSuites); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsCipherSuites "+err.Error()))
+ }
+ return errs
+}
+
type GeneralConfig struct {
config.BaseConfig
@@ -93,12 +204,20 @@
Admin *admin.Admin `json:"admin"`
// DeployMode-specific configuration
Runtime *runtime.RuntimeConfig `json:"runtime,omitempty"`
+ // API Server configuration
+ ApiServer *api_server.ApiServerConfig `json:"apiServer,omitempty"`
// Multizone Config
Multizone *multizone.MultizoneConfig `json:"multizone,omitempty"`
// Default dubbo entities configuration
Defaults *Defaults `json:"defaults,omitempty"`
+ // Metrics configuration
+ Metrics *Metrics `json:"metrics,omitempty"`
+ // Intercommunication CP configuration
+ InterCp intercp.InterCpConfig `json:"interCp"`
// Diagnostics configuration
Diagnostics *diagnostics.DiagnosticsConfig `json:"diagnostics,omitempty"`
+ // Access Control configuration
+ Access access.AccessConfig `json:"access"`
// Proxy holds configuration for proxies
Proxy xds.Proxy `json:"proxy"`
// Dataplane Server configuration
@@ -172,6 +291,7 @@
DubboConfig: dubbo.DefaultServiceNameMappingConfig(),
EventBus: eventbus.Default(),
DDSEventBasedWatchdog: DefaultEventBasedWatchdog(),
+ ApiServer: api_server.DefaultApiServerConfig(),
}
}
diff --git a/pkg/config/intercp/config.go b/pkg/config/intercp/config.go
new file mode 100644
index 0000000..018ee67
--- /dev/null
+++ b/pkg/config/intercp/config.go
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package intercp
+
+import (
+ "time"
+
+ "github.com/asaskevich/govalidator"
+ "github.com/pkg/errors"
+ "go.uber.org/multierr"
+
+ config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+func DefaultInterCpConfig() InterCpConfig {
+ return InterCpConfig{
+ Catalog: CatalogConfig{
+ InstanceAddress: "", // autoconfigured
+ HeartbeatInterval: config_types.Duration{Duration: 5 * time.Second},
+ WriterInterval: config_types.Duration{Duration: 15 * time.Second},
+ },
+ Server: InterCpServerConfig{
+ Port: 5683,
+ TlsMinVersion: "TLSv1_2",
+ TlsCipherSuites: []string{},
+ },
+ }
+}
+
+type InterCpConfig struct {
+ // Catalog configuration. Catalog keeps a record of all live CP instances in the zone.
+ Catalog CatalogConfig `json:"catalog"`
+ // Intercommunication CP server configuration
+ Server InterCpServerConfig `json:"server"`
+}
+
+func (i *InterCpConfig) Validate() error {
+ if err := i.Server.Validate(); err != nil {
+ return errors.Wrap(err, ".Server validation failed")
+ }
+ if err := i.Catalog.Validate(); err != nil {
+ return errors.Wrap(err, ".Catalog validation failed")
+ }
+ return nil
+}
+
+type CatalogConfig struct {
+ // InstanceAddress indicates an address on which other control planes can communicate with this CP
+ // If empty then it's autoconfigured by taking the first IP of the nonloopback network interface.
+ InstanceAddress string `json:"instanceAddress" envconfig:"kuma_inter_cp_catalog_instance_address"`
+ // Interval on which CP will send heartbeat to a leader.
+ HeartbeatInterval config_types.Duration `json:"heartbeatInterval" envconfig:"kuma_inter_cp_catalog_heartbeat_interval"`
+ // Interval on which CP will write all instances to a catalog.
+ WriterInterval config_types.Duration `json:"writerInterval" envconfig:"kuma_inter_cp_catalog_writer_interval"`
+}
+
+func (i *CatalogConfig) Validate() error {
+ if i.InstanceAddress != "" && !govalidator.IsDNSName(i.InstanceAddress) && !govalidator.IsIP(i.InstanceAddress) {
+ return errors.New(".InstanceAddress has to be valid IP or DNS address")
+ }
+ return nil
+}
+
+type InterCpServerConfig struct {
+ // Port on which Intercommunication CP server will listen
+ Port uint16 `json:"port" envconfig:"kuma_inter_cp_server_port"`
+ // TlsMinVersion defines the minimum TLS version to be used
+ TlsMinVersion string `json:"tlsMinVersion" envconfig:"kuma_inter_cp_server_tls_min_version"`
+ // TlsMaxVersion defines the maximum TLS version to be used
+ TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"kuma_inter_cp_server_tls_max_version"`
+ // TlsCipherSuites defines the list of ciphers to use
+ TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"kuma_inter_cp_server_tls_cipher_suites"`
+}
+
+func (i *InterCpServerConfig) Validate() error {
+ var errs error
+ if i.Port == 0 {
+ errs = multierr.Append(errs, errors.New(".Port cannot be zero"))
+ }
+ if _, err := config_types.TLSVersion(i.TlsMinVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMinVersion "+err.Error()))
+ }
+ if _, err := config_types.TLSVersion(i.TlsMaxVersion); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsMaxVersion "+err.Error()))
+ }
+ if _, err := config_types.TLSCiphers(i.TlsCipherSuites); err != nil {
+ errs = multierr.Append(errs, errors.New(".TlsCipherSuites "+err.Error()))
+ }
+ return errs
+}
diff --git a/pkg/core/access/errors.go b/pkg/core/access/errors.go
new file mode 100644
index 0000000..5ec4bfb
--- /dev/null
+++ b/pkg/core/access/errors.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type AccessDeniedError struct {
+ Reason string
+}
+
+func (a *AccessDeniedError) Error() string {
+ return "access denied: " + a.Reason
+}
+
+func (a *AccessDeniedError) Is(err error) bool {
+ return reflect.TypeOf(a) == reflect.TypeOf(err)
+}
+
+func Validate(usernames map[string]struct{}, groups map[string]struct{}, user user.User, action string) error {
+ if _, ok := usernames[user.Name]; ok {
+ return nil
+ }
+ for _, group := range user.Groups {
+ if _, ok := groups[group]; ok {
+ return nil
+ }
+ }
+ return &AccessDeniedError{Reason: fmt.Sprintf("user %q cannot access %s", user, action)}
+}
diff --git a/pkg/core/access/metadata.go b/pkg/core/access/metadata.go
new file mode 100644
index 0000000..11f2925
--- /dev/null
+++ b/pkg/core/access/metadata.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type ControlPlaneMetadataAccess interface {
+ ValidateView(ctx context.Context, user user.User) error
+}
+
+type staticMetadataAccess struct {
+ usernames map[string]struct{}
+ groups map[string]struct{}
+}
+
+func NewStaticControlPlaneMetadataAccess(cfg config_access.ControlPlaneMetadataStaticAccessConfig) ControlPlaneMetadataAccess {
+ s := &staticMetadataAccess{
+ usernames: make(map[string]struct{}, len(cfg.Users)),
+ groups: make(map[string]struct{}, len(cfg.Groups)),
+ }
+ for _, u := range cfg.Users {
+ s.usernames[u] = struct{}{}
+ }
+ for _, g := range cfg.Groups {
+ s.groups[g] = struct{}{}
+ }
+ return s
+}
+
+func (s staticMetadataAccess) ValidateView(ctx context.Context, user user.User) error {
+ return Validate(s.usernames, s.groups, user, "control-plane metadata")
+}
diff --git a/pkg/core/bootstrap/bootstrap.go b/pkg/core/bootstrap/bootstrap.go
index fc5bf34..b5248bc 100644
--- a/pkg/core/bootstrap/bootstrap.go
+++ b/pkg/core/bootstrap/bootstrap.go
@@ -23,24 +23,28 @@
"net/url"
"strings"
"sync"
-)
-import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/config/instance"
"dubbo.apache.org/dubbo-go/v3/config_center"
-
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ resources_access "github.com/apache/dubbo-kubernetes/pkg/core/resources/access"
+ "github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+ envoyadmin_access "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/access"
+ tokens_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/access"
+ zone_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone/access"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
"github.com/pkg/errors"
kube_ctrl "sigs.k8s.io/controller-runtime"
-)
-import (
dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+
config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
"github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
"github.com/apache/dubbo-kubernetes/pkg/core"
+
config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
"github.com/apache/dubbo-kubernetes/pkg/core/consts"
"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
@@ -48,28 +52,42 @@
"github.com/apache/dubbo-kubernetes/pkg/core/governance"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/condition_route"
+
dataplane_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dataplane"
"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dynamic_config"
+
mapping_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mapping"
+
mesh_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
+
metadata_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/metadata"
"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/tag_route"
"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/zone"
+
core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+
dubbo_registry "github.com/apache/dubbo-kubernetes/pkg/core/registry"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+
core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+
core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+
core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
"github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
"github.com/apache/dubbo-kubernetes/pkg/events"
+
k8s_extensions "github.com/apache/dubbo-kubernetes/pkg/plugins/extensions/k8s"
+
mesh_cache "github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
+
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
xds_server "github.com/apache/dubbo-kubernetes/pkg/xds/server"
)
@@ -118,9 +136,16 @@
builder.WithDataSourceLoader(datasource.NewDataSourceLoader(builder.ReadOnlyResourceManager()))
+ //initializeCAManager
+ if err := initializeCaManagers(builder); err != nil {
+ return nil, err
+ }
+
leaderInfoComponent := &component.LeaderInfoComponent{}
builder.WithLeaderInfo(leaderInfoComponent)
+ caProvider, _ := secrets.NewCaProvider(builder.CaManagers())
+ builder.WithCAProvider(caProvider)
builder.WithDpServer(server.NewDpServer(*cfg.DpServer, func(writer http.ResponseWriter, request *http.Request) bool {
return true
}))
@@ -129,6 +154,28 @@
ddsContext := dds_context.DefaultContext(appCtx, resourceManager, cfg)
builder.WithDDSContext(ddsContext)
+ if cfg.DeployMode == config_core.UniversalMode {
+ //TODO: universalMode EnvoyAdminClient
+ } else {
+ builder.WithEnvoyAdminClient(admin.NewEnvoyAdminClient(
+ resourceManager,
+ builder.CaManagers(),
+ builder.Config().GetEnvoyAdminPort(),
+ ))
+ }
+
+ builder.WithAccess(core_runtime.Access{
+ ResourceAccess: resources_access.NewAdminResourceAccess(builder.Config().Access.Static.AdminResources),
+ DataplaneTokenAccess: tokens_access.NewStaticGenerateDataplaneTokenAccess(builder.Config().Access.Static.GenerateDPToken),
+ ZoneTokenAccess: zone_access.NewStaticZoneTokenAccess(builder.Config().Access.Static.GenerateZoneToken),
+ EnvoyAdminAccess: envoyadmin_access.NewStaticEnvoyAdminAccess(
+ builder.Config().Access.Static.ViewConfigDump,
+ builder.Config().Access.Static.ViewStats,
+ builder.Config().Access.Static.ViewClusters,
+ ),
+ ControlPlaneMetadataAccess: access.NewStaticControlPlaneMetadataAccess(builder.Config().Access.Static.ControlPlaneMetadata),
+ })
+
if err := initializeMeshCache(builder); err != nil {
return nil, err
}
@@ -486,3 +533,14 @@
builder.WithMeshCache(meshSnapshotCache)
return nil
}
+
+func initializeCaManagers(builder *core_runtime.Builder) error {
+ for pluginName, caPlugin := range core_plugins.Plugins().CaPlugins() {
+ caManager, err := caPlugin.NewCaManager(builder, nil)
+ if err != nil {
+ return errors.Wrapf(err, "could not create CA manager for plugin %q", pluginName)
+ }
+ builder.WithCaManager(string(pluginName), caManager)
+ }
+ return nil
+}
diff --git a/pkg/core/ca/manager.go b/pkg/core/ca/manager.go
new file mode 100644
index 0000000..8ca456e
--- /dev/null
+++ b/pkg/core/ca/manager.go
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ca
+
+import mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+
+import (
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+type Cert = []byte
+
+type KeyPair = tls.KeyPair
+
+// Manager manages CAs by creating CAs and generating certificate. It is created per CA type and then may be used for different CA instances of the same type
+type Manager interface {
+ // ValidateBackend validates that backend configuration is correct
+ ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error
+ // EnsureBackends ensures the given CA backends managed by this manager are available.
+ // Since the secrets are now explicitly children of mesh we need to pass the whole mesh object so that we can properly set the owner.
+ EnsureBackends(ctx context.Context, mesh model.Resource, backends []*mesh_proto.CertificateAuthorityBackend) error
+ // UsedSecrets returns a list of secrets that are used by the manager
+ UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error)
+
+ // GetRootCert returns root certificates of the CA
+ GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]Cert, error)
+ // GenerateDataplaneCert generates cert for a dataplane with service tags
+ GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (KeyPair, error)
+}
+
+// Managers hold Manager instance for each type of backend available (by default: builtin, provided)
+type Managers = map[string]Manager
diff --git a/pkg/core/managers/apis/mesh/mesh_helpers.go b/pkg/core/managers/apis/mesh/mesh_helpers.go
new file mode 100644
index 0000000..596d0a3
--- /dev/null
+++ b/pkg/core/managers/apis/mesh/mesh_helpers.go
@@ -0,0 +1,28 @@
+package mesh
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+)
+
+func EnsureCAs(ctx context.Context, caManagers core_ca.Managers, mesh *core_mesh.MeshResource, meshName string) error {
+ backendsForType := make(map[string][]*v1alpha1.CertificateAuthorityBackend)
+ for _, backend := range mesh.Spec.GetMtls().GetBackends() {
+ backendsForType[backend.Type] = append(backendsForType[backend.Type], backend)
+ }
+ for typ, backends := range backendsForType {
+ caManager, exist := caManagers[typ]
+ if !exist { // this should be caught by validator earlier
+ return errors.Errorf("CA manager for type %s does not exist", typ)
+ }
+ if err := caManager.EnsureBackends(ctx, mesh, backends); err != nil {
+ return errors.Wrapf(err, "could not ensure CA backends of type %s", typ)
+ }
+ }
+ return nil
+}
diff --git a/pkg/core/managers/apis/mesh/mesh_manager.go b/pkg/core/managers/apis/mesh/mesh_manager.go
index d1f79b4..4581c91 100644
--- a/pkg/core/managers/apis/mesh/mesh_manager.go
+++ b/pkg/core/managers/apis/mesh/mesh_manager.go
@@ -19,6 +19,8 @@
import (
"context"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
"time"
)
@@ -178,3 +180,26 @@
}
return meshes, nil
}
+
+func ValidateMTLSBackends(ctx context.Context, caManagers core_ca.Managers, name string, resource *core_mesh.MeshResource) validators.ValidationError {
+ verr := validators.ValidationError{}
+ path := validators.RootedAt("mtls").Field("backends")
+
+ for idx, backend := range resource.Spec.GetMtls().GetBackends() {
+ caManager, exist := caManagers[backend.Type]
+ if !exist {
+ verr.AddViolationAt(path.Index(idx).Field("type"), "could not find installed plugin for this type")
+ return verr
+ }
+ //TODO:要不要支持skipvalidation?
+ if err := caManager.ValidateBackend(ctx, name, backend); err != nil {
+ if configErr, ok := err.(*validators.ValidationError); ok {
+ verr.AddErrorAt(path.Index(idx).Field("conf"), *configErr)
+ } else {
+ verr.AddViolationAt(path, err.Error())
+ }
+ }
+
+ }
+ return verr
+}
diff --git a/pkg/core/managers/apis/mesh/mesh_validator.go b/pkg/core/managers/apis/mesh/mesh_validator.go
index a195b24..387ece1 100644
--- a/pkg/core/managers/apis/mesh/mesh_validator.go
+++ b/pkg/core/managers/apis/mesh/mesh_validator.go
@@ -19,6 +19,10 @@
import (
"context"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ "github.com/pkg/errors"
)
import (
@@ -30,3 +34,59 @@
ValidateUpdate(ctx context.Context, previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) error
ValidateDelete(ctx context.Context, name string) error
}
+
+type meshValidator struct {
+ CaManagers core_ca.Managers
+ Store core_store.ResourceStore
+}
+
+func NewMeshValidator(caManagers core_ca.Managers, store core_store.ResourceStore) MeshValidator {
+ return &meshValidator{
+ CaManagers: caManagers,
+ Store: store,
+ }
+}
+
+func (m *meshValidator) ValidateCreate(ctx context.Context, name string, resource *core_mesh.MeshResource) error {
+ var verr validators.ValidationError
+ if len(name) > 63 {
+ verr.AddViolation("name", "cannot be longer than 63 characters")
+ }
+ verr.Add(ValidateMTLSBackends(ctx, m.CaManagers, name, resource))
+ return verr.OrNil()
+}
+
+func (m *meshValidator) ValidateDelete(ctx context.Context, name string) error {
+ if err := ValidateNoActiveDP(ctx, name, m.Store); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (m *meshValidator) ValidateUpdate(ctx context.Context, previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) error {
+ var verr validators.ValidationError
+ verr.Add(m.validateMTLSBackendChange(previousMesh, newMesh))
+ verr.Add(ValidateMTLSBackends(ctx, m.CaManagers, newMesh.Meta.GetName(), newMesh))
+ return verr.OrNil()
+}
+
+func ValidateNoActiveDP(ctx context.Context, name string, store core_store.ResourceStore) error {
+ dps := core_mesh.DataplaneResourceList{}
+ validationErr := &validators.ValidationError{}
+ if err := store.List(ctx, &dps, core_store.ListByMesh(name)); err != nil {
+ return errors.Wrap(err, "unable to list Dataplanes")
+ }
+ if len(dps.Items) != 0 {
+ validationErr.AddViolation("mesh", "unable to delete mesh, there are still some dataplanes attached")
+ return validationErr
+ }
+ return nil
+}
+
+func (m *meshValidator) validateMTLSBackendChange(previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) validators.ValidationError {
+ verr := validators.ValidationError{}
+ if previousMesh.MTLSEnabled() && newMesh.MTLSEnabled() && previousMesh.Spec.GetMtls().GetEnabledBackend() != newMesh.Spec.GetMtls().GetEnabledBackend() {
+ verr.AddViolation("mtls.enabledBackend", "Changing CA when mTLS is enabled is forbidden. Disable mTLS first and then change the CA")
+ }
+ return verr
+}
diff --git a/pkg/core/plugins/interfaces.go b/pkg/core/plugins/interfaces.go
index 63e0a40..4ff79ae 100644
--- a/pkg/core/plugins/interfaces.go
+++ b/pkg/core/plugins/interfaces.go
@@ -18,16 +18,18 @@
package plugins
import (
- "github.com/pkg/errors"
-)
+ "context"
-import (
+ "github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
"github.com/apache/dubbo-kubernetes/pkg/events"
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+ "github.com/pkg/errors"
)
type Plugin interface{}
@@ -96,5 +98,52 @@
type CaPlugin interface {
Plugin
- NewCaManager(PluginContext, PluginConfig)
+ NewCaManager(PluginContext, PluginConfig) (core_ca.Manager, error)
+}
+
+// AuthnAPIServerPlugin is responsible for providing authenticator for API Server.
+
+type SecretStorePlugin interface {
+ Plugin
+ NewSecretStore(PluginContext, PluginConfig) (secret_store.SecretStore, error)
+}
+
+// AuthnAPIServerPlugin is responsible for providing authenticator for API Server.
+type AuthnAPIServerPlugin interface {
+ Plugin
+ NewAuthenticator(PluginContext) (authn.Authenticator, error)
+}
+
+type MatchedPoliciesOption func(*MatchedPoliciesConfig)
+
+func IncludeShadow() MatchedPoliciesOption {
+ return func(cfg *MatchedPoliciesConfig) {
+ cfg.IncludeShadow = true
+ }
+}
+
+type MatchedPoliciesConfig struct {
+ IncludeShadow bool
+}
+
+func NewMatchedPoliciesConfig(opts ...MatchedPoliciesOption) *MatchedPoliciesConfig {
+ cfg := &MatchedPoliciesConfig{}
+ for _, opt := range opts {
+ opt(cfg)
+ }
+ return cfg
+}
+
+type EgressPolicyPlugin interface {
+ PolicyPlugin
+ // EgressMatchedPolicies returns all the policies of the plugins' type matching the external service that
+ // should be applied on the zone egress.
+ EgressMatchedPolicies(tags map[string]string, resources xds_context.Resources, opts ...MatchedPoliciesOption) (core_xds.TypedMatchingPolicies, error)
+}
+
+// ProxyPlugin a plugin to modify the proxy. This happens before any `PolicyPlugin` or any envoy generation. and it is applied both for Dataplanes and ZoneProxies
+type ProxyPlugin interface {
+ Plugin
+ // Apply mutate the proxy as needed.
+ Apply(ctx context.Context, meshCtx xds_context.MeshContext, proxy *core_xds.Proxy) error
}
diff --git a/pkg/core/plugins/registry.go b/pkg/core/plugins/registry.go
index ba6840a..bca54c0 100644
--- a/pkg/core/plugins/registry.go
+++ b/pkg/core/plugins/registry.go
@@ -35,6 +35,7 @@
runtimePlugin pluginType = "runtime"
policyPlugin pluginType = "policy"
caPlugin pluginType = "ca"
+ secretStorePlugin pluginType = "secret-store"
)
type PluginName string
@@ -48,7 +49,8 @@
Nacos PluginName = "nacos"
MySQL PluginName = "mysql"
- CaBuiltin PluginName = "builtin"
+ CaBuiltin PluginName = "builtin"
+ CaProvided PluginName = "provided"
)
type RegisteredPolicyPlugin struct {
@@ -62,6 +64,8 @@
ConfigStore(name PluginName) (ConfigStorePlugin, error)
RuntimePlugins() map[PluginName]RuntimePlugin
PolicyPlugins([]PluginName) []RegisteredPolicyPlugin
+ CaPlugins() map[PluginName]CaPlugin
+ SecretStore(name PluginName) (SecretStorePlugin, error)
}
type RegistryMutator interface {
@@ -80,6 +84,8 @@
configStore: make(map[PluginName]ConfigStorePlugin),
runtime: make(map[PluginName]RuntimePlugin),
registeredPolicies: make(map[PluginName]PolicyPlugin),
+ ca: make(map[PluginName]CaPlugin),
+ secretStore: make(map[PluginName]SecretStorePlugin),
}
}
@@ -91,6 +97,8 @@
configStore map[PluginName]ConfigStorePlugin
runtime map[PluginName]RuntimePlugin
registeredPolicies map[PluginName]PolicyPlugin
+ ca map[PluginName]CaPlugin
+ secretStore map[PluginName]SecretStorePlugin
}
func (r *registry) ResourceStore(name PluginName) (ResourceStorePlugin, error) {
@@ -109,6 +117,10 @@
}
}
+func (r *registry) CaPlugins() map[PluginName]CaPlugin {
+ return r.ca
+}
+
func (r *registry) RuntimePlugins() map[PluginName]RuntimePlugin {
return r.runtime
}
@@ -175,6 +187,12 @@
}
r.registeredPolicies[name] = policy
}
+ if cp, ok := plugin.(CaPlugin); ok {
+ if old, exists := r.ca[name]; exists {
+ return pluginAlreadyRegisteredError(caPlugin, name, old, cp)
+ }
+ r.ca[name] = cp
+ }
return nil
}
@@ -186,3 +204,11 @@
return errors.Errorf("plugin with type=%q and name=%s has already been registered: old=%#v new=%#v",
typ, name, old, new)
}
+
+func (r *registry) SecretStore(name PluginName) (SecretStorePlugin, error) {
+ if p, ok := r.secretStore[name]; ok {
+ return p, nil
+ } else {
+ return nil, noSuchPluginError(secretStorePlugin, name)
+ }
+}
diff --git a/pkg/core/resources/access/admin_resource_access.go b/pkg/core/resources/access/admin_resource_access.go
new file mode 100644
index 0000000..f58f9d2
--- /dev/null
+++ b/pkg/core/resources/access/admin_resource_access.go
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+ "fmt"
+
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type adminResourceAccess struct {
+ usernames map[string]struct{}
+ groups map[string]struct{}
+}
+
+func NewAdminResourceAccess(cfg config_access.AdminResourcesStaticAccessConfig) ResourceAccess {
+ a := &adminResourceAccess{
+ usernames: make(map[string]struct{}),
+ groups: make(map[string]struct{}),
+ }
+ for _, user := range cfg.Users {
+ a.usernames[user] = struct{}{}
+ }
+
+ for _, group := range cfg.Groups {
+ a.groups[group] = struct{}{}
+ }
+ return a
+}
+
+var _ ResourceAccess = &adminResourceAccess{}
+
+func (a *adminResourceAccess) ValidateCreate(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+ return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateUpdate(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+ return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateDelete(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+ return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateList(ctx context.Context, _ string, descriptor model.ResourceTypeDescriptor, user user.User) error {
+ return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateGet(ctx context.Context, _ model.ResourceKey, descriptor model.ResourceTypeDescriptor, user user.User) error {
+ return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (r *adminResourceAccess) validateAdminAccess(_ context.Context, u user.User, descriptor model.ResourceTypeDescriptor) error {
+ if !descriptor.AdminOnly {
+ return nil
+ }
+ return access.Validate(r.usernames, r.groups, u, fmt.Sprintf("the resource of type %q", descriptor.Name))
+}
diff --git a/pkg/core/resources/access/resource_access.go b/pkg/core/resources/access/resource_access.go
new file mode 100644
index 0000000..2a6c679
--- /dev/null
+++ b/pkg/core/resources/access/resource_access.go
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type ResourceAccess interface {
+ ValidateCreate(ctx context.Context, key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+ ValidateUpdate(ctx context.Context, key model.ResourceKey, currentSpec model.ResourceSpec, newSpec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+ ValidateDelete(ctx context.Context, key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+ ValidateList(ctx context.Context, mesh string, desc model.ResourceTypeDescriptor, user user.User) error
+ ValidateGet(ctx context.Context, key model.ResourceKey, desc model.ResourceTypeDescriptor, user user.User) error
+}
diff --git a/pkg/core/resources/apis/system/global_secret.go b/pkg/core/resources/apis/system/global_secret.go
new file mode 100644
index 0000000..45c6e1c
--- /dev/null
+++ b/pkg/core/resources/apis/system/global_secret.go
@@ -0,0 +1,122 @@
+package system
+
+import (
+ "errors"
+
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+)
+
+const (
+ GlobalSecretType model.ResourceType = "GlobalSecret"
+)
+
+var _ model.Resource = &GlobalSecretResource{}
+
+type GlobalSecretResource struct {
+ Meta model.ResourceMeta
+ Spec *system_proto.Secret
+}
+
+func NewGlobalSecretResource() *GlobalSecretResource {
+ return &GlobalSecretResource{
+ Spec: &system_proto.Secret{},
+ }
+}
+
+func (t *GlobalSecretResource) GetMeta() model.ResourceMeta {
+ return t.Meta
+}
+
+func (t *GlobalSecretResource) SetMeta(m model.ResourceMeta) {
+ t.Meta = m
+}
+
+func (t *GlobalSecretResource) GetSpec() model.ResourceSpec {
+ return t.Spec
+}
+
+func (t *GlobalSecretResource) SetSpec(spec model.ResourceSpec) error {
+ value, ok := spec.(*system_proto.Secret)
+ if !ok {
+ return errors.New("invalid type of spec")
+ } else {
+ t.Spec = value
+ return nil
+ }
+}
+
+func (t *GlobalSecretResource) GetStatus() model.ResourceStatus {
+ return nil
+}
+
+func (t *GlobalSecretResource) SetStatus(_ model.ResourceStatus) error {
+ return errors.New("status not supported")
+}
+
+func (t *GlobalSecretResource) Validate() error {
+ return nil
+}
+
+func (t *GlobalSecretResource) Descriptor() model.ResourceTypeDescriptor {
+ return GlobalSecretResourceTypeDescriptor
+}
+
+var _ model.ResourceList = &GlobalSecretResourceList{}
+
+type GlobalSecretResourceList struct {
+ Items []*GlobalSecretResource
+ Pagination model.Pagination
+}
+
+func (l *GlobalSecretResourceList) GetItems() []model.Resource {
+ res := make([]model.Resource, len(l.Items))
+ for i, elem := range l.Items {
+ res[i] = elem
+ }
+ return res
+}
+
+func (l *GlobalSecretResourceList) GetItemType() model.ResourceType {
+ return GlobalSecretType
+}
+
+func (l *GlobalSecretResourceList) NewItem() model.Resource {
+ return NewGlobalSecretResource()
+}
+
+func (l *GlobalSecretResourceList) AddItem(r model.Resource) error {
+ if trr, ok := r.(*GlobalSecretResource); ok {
+ l.Items = append(l.Items, trr)
+ return nil
+ } else {
+ return model.ErrorInvalidItemType((*GlobalSecretResource)(nil), r)
+ }
+}
+
+func (l *GlobalSecretResourceList) GetPagination() *model.Pagination {
+ return &l.Pagination
+}
+
+func (l *GlobalSecretResourceList) SetPagination(p model.Pagination) {
+ l.Pagination = p
+}
+
+var GlobalSecretResourceTypeDescriptor model.ResourceTypeDescriptor
+
+func init() {
+ GlobalSecretResourceTypeDescriptor = model.ResourceTypeDescriptor{
+ Name: GlobalSecretType,
+ Resource: NewGlobalSecretResource(),
+ ResourceList: &GlobalSecretResourceList{},
+ ReadOnly: false,
+ AdminOnly: true,
+ Scope: model.ScopeGlobal,
+ DDSFlags: model.GlobalToAllZonesFlag,
+ WsPath: "global-secrets",
+ DubboctlArg: "global-secret",
+ DubboctlListArg: "global-secrets",
+ }
+ registry.RegisterType(GlobalSecretResourceTypeDescriptor)
+}
diff --git a/pkg/core/resources/model/resource.go b/pkg/core/resources/model/resource.go
index 01b425c..5bdc2f5 100644
--- a/pkg/core/resources/model/resource.go
+++ b/pkg/core/resources/model/resource.go
@@ -103,6 +103,7 @@
}
type ResourceSpec interface{}
+type ResourceStatus interface{}
type Resource interface {
GetMeta() ResourceMeta
diff --git a/pkg/core/rest/errors/error_handler.go b/pkg/core/rest/errors/error_handler.go
new file mode 100644
index 0000000..60fd123
--- /dev/null
+++ b/pkg/core/rest/errors/error_handler.go
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package errors
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/emicklei/go-restful/v3"
+ "github.com/go-logr/logr"
+ "go.opentelemetry.io/otel/trace"
+
+ api_server_types "github.com/apache/dubbo-kubernetes/pkg/api-server/types"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model/rest"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors/types"
+ "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ kuma_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+func HandleError(ctx context.Context, response *restful.Response, err error, title string) {
+ log := kuma_log.AddFieldsFromCtx(logr.FromContextOrDiscard(ctx), ctx, context.Background())
+
+ var kumaErr *types.Error
+ switch {
+ case store.IsResourceNotFound(err) || errors.Is(err, &NotFound{}):
+ kumaErr = &types.Error{
+ Status: 404,
+ Title: title,
+ Detail: "Not found",
+ }
+ case errors.Is(err, &rest.InvalidResourceError{}), errors.Is(err, ®istry.InvalidResourceTypeError{}), errors.Is(err, &store.PreconditionError{}), errors.Is(err, &BadRequest{}):
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: "Bad Request",
+ Detail: err.Error(),
+ }
+ case manager.IsMeshNotFound(err):
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: title,
+ Detail: "Mesh is not found",
+ InvalidParameters: []types.InvalidParameter{
+ {
+ Field: "mesh",
+ Reason: fmt.Sprintf("mesh of name %s is not found", err.(*manager.MeshNotFoundError).Mesh),
+ },
+ },
+ }
+ case validators.IsValidationError(err):
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: title,
+ Detail: "Resource is not valid",
+ }
+ for _, violation := range err.(*validators.ValidationError).Violations {
+ kumaErr.InvalidParameters = append(kumaErr.InvalidParameters, types.InvalidParameter{
+ Field: violation.Field,
+ Reason: violation.Message,
+ })
+ }
+ case err == store.ErrorInvalidOffset:
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: title,
+ Detail: "Invalid offset",
+ InvalidParameters: []types.InvalidParameter{
+ {
+ Field: "offset",
+ Reason: "Invalid format",
+ },
+ },
+ }
+ case errors.Is(err, &api_server_types.InvalidPageSizeError{}):
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: title,
+ Detail: "Invalid page size",
+ InvalidParameters: []types.InvalidParameter{
+ {
+ Field: "size",
+ Reason: err.Error(),
+ },
+ },
+ }
+ case tokens.IsSigningKeyNotFound(err):
+ kumaErr = &types.Error{
+ Status: 404,
+ Title: "Signing Key not found",
+ Detail: err.Error(),
+ }
+ case errors.Is(err, &MethodNotAllowed{}):
+ kumaErr = &types.Error{
+ Status: 405,
+ Title: "Method not Allowed",
+ Detail: err.Error(),
+ }
+ case errors.Is(err, &Conflict{}) || errors.Is(err, &store.ResourceConflictError{}):
+ kumaErr = &types.Error{
+ Status: 409,
+ Title: "Conflict",
+ Detail: err.Error(),
+ }
+ case errors.Is(err, &ServiceUnavailable{}):
+ kumaErr = &types.Error{
+ Status: 503,
+ Title: "Service unavailable",
+ Detail: err.Error(),
+ }
+ case errors.Is(err, &access.AccessDeniedError{}):
+ var accessErr *access.AccessDeniedError
+ errors.As(err, &accessErr)
+ kumaErr = &types.Error{
+ Status: 403,
+ Title: "Access Denied",
+ Detail: accessErr.Reason,
+ }
+ case errors.Is(err, &Unauthenticated{}):
+ var unauthenticated *Unauthenticated
+ errors.As(err, &unauthenticated)
+ kumaErr = &types.Error{
+ Status: 401,
+ Title: title,
+ Detail: unauthenticated.Error(),
+ }
+ case err == tokens.IssuerDisabled:
+ kumaErr = &types.Error{
+ Status: 400,
+ Title: title,
+ Detail: err.Error(),
+ }
+ case errors.As(err, &kumaErr):
+ default:
+ log.Error(err, title)
+ kumaErr = &types.Error{
+ Status: 500,
+ Title: title,
+ Detail: "Internal Server Error",
+ }
+ }
+ if ctx != nil {
+ span := trace.SpanFromContext(ctx)
+ span.RecordError(err, trace.WithStackTrace(true))
+ if span.IsRecording() {
+ kumaErr.Instance = span.SpanContext().TraceID().String()
+ }
+ }
+ // Fix to handle legacy errors
+ kumaErr.Type = "/std-errors"
+ kumaErr.Details = kumaErr.Detail
+ for _, ip := range kumaErr.InvalidParameters {
+ kumaErr.Causes = append(kumaErr.Causes, types.Cause{Field: ip.Field, Message: ip.Reason})
+ }
+ if err := response.WriteHeaderAndJson(kumaErr.Status, kumaErr, "application/json"); err != nil {
+ log.Error(err, "Could not write the error response")
+ }
+}
diff --git a/pkg/core/rest/errors/errors.go b/pkg/core/rest/errors/errors.go
new file mode 100644
index 0000000..9ff372d
--- /dev/null
+++ b/pkg/core/rest/errors/errors.go
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package errors
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type Unauthenticated struct{}
+
+func (e *Unauthenticated) Error() string {
+ return "Unauthenticated"
+}
+
+type MethodNotAllowed struct{}
+
+func (e *MethodNotAllowed) Error() string {
+ return "Method not allowed"
+}
+
+type Conflict struct{}
+
+func (e *Conflict) Error() string {
+ return "Conflict"
+}
+
+type ServiceUnavailable struct{}
+
+func (e *ServiceUnavailable) Error() string {
+ return "Service unavailable"
+}
+
+type BadRequest struct {
+ msg string
+}
+
+func NewBadRequestError(msg string) error {
+ return &BadRequest{msg: msg}
+}
+
+func (e *BadRequest) Error() string {
+ if e.msg == "" {
+ return "bad request"
+ }
+ return fmt.Sprintf("bad request: %s", e.msg)
+}
+
+func (e *BadRequest) Is(err error) bool {
+ return reflect.TypeOf(e) == reflect.TypeOf(err)
+}
+
+func NewNotFoundError(msg string) error {
+ return &NotFound{msg: msg}
+}
+
+type NotFound struct {
+ msg string
+}
+
+func (e *NotFound) Error() string {
+ if e.msg == "" {
+ return "not found"
+ }
+ return fmt.Sprintf("not found: %s", e.msg)
+}
+
+func (e *NotFound) Is(err error) bool {
+ return reflect.TypeOf(e) == reflect.TypeOf(err)
+}
diff --git a/pkg/core/rest/errors/types/error.go b/pkg/core/rest/errors/types/error.go
new file mode 100644
index 0000000..5e4dade
--- /dev/null
+++ b/pkg/core/rest/errors/types/error.go
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package types
+
+import "fmt"
+
+// Error following https://kong-aip.netlify.app/aip/193/
+type Error struct {
+ // Type a unique identifier for this error.
+ Type string `json:"type"`
+ // Status The HTTP status code of the error.
+ Status int `json:"status"`
+ // Title A short, human-readable summary of the problem.
+ // It should not change between occurrences of a problem, except for localization.
+ // Should be provided as "Sentence case" for direct use in the UI
+ Title string `json:"title"`
+ // Detail A human readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+ // Instance Used to return the correlation ID back to the user if present
+ Instance string `json:"instance,omitempty"`
+ // InvalidParameters
+ InvalidParameters []InvalidParameter `json:"invalid_parameters,omitempty"`
+
+ // Deprecated
+ Details string `json:"details"`
+ // Deprecated
+ Causes []Cause `json:"causes,omitempty"`
+}
+
+type InvalidParameter struct {
+ Field string `json:"field"`
+ Reason string `json:"reason"`
+ Rule string `json:"rule,omitempty"`
+ Choices []string `json:"choices,omitempty"`
+}
+
+func (e *Error) Error() string {
+ msg := fmt.Sprintf("%s (%s)", e.Title, e.Detail)
+ for _, cause := range e.InvalidParameters {
+ msg += fmt.Sprintf(";%s=%s ", cause.Field, cause.Reason)
+ }
+ return msg
+}
+
+type Cause struct {
+ Field string `json:"field"`
+ Message string `json:"message"`
+}
diff --git a/pkg/core/runtime/builder.go b/pkg/core/runtime/builder.go
index 6b3a3e9..8511da1 100644
--- a/pkg/core/runtime/builder.go
+++ b/pkg/core/runtime/builder.go
@@ -23,29 +23,36 @@
"os"
"sync"
"time"
-)
-import (
"dubbo.apache.org/dubbo-go/v3/config_center"
"dubbo.apache.org/dubbo-go/v3/metadata/report"
+ "github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
+
dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
-
"github.com/pkg/errors"
-)
-import (
+ api_server "github.com/apache/dubbo-kubernetes/pkg/api-server/customization"
+
dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
"github.com/apache/dubbo-kubernetes/pkg/core"
+
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+
config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
"github.com/apache/dubbo-kubernetes/pkg/core/dns/lookup"
"github.com/apache/dubbo-kubernetes/pkg/core/governance"
"github.com/apache/dubbo-kubernetes/pkg/core/reg_client"
"github.com/apache/dubbo-kubernetes/pkg/core/registry"
+
core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+
core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
+
dp_server "github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
"github.com/apache/dubbo-kubernetes/pkg/events"
"github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
@@ -57,6 +64,7 @@
ResourceStore() core_store.CustomizableResourceStore
Transactions() core_store.Transactions
ConfigStore() core_store.ResourceStore
+ DataSourceLoader() datasource.Loader
ResourceManager() core_manager.CustomizableResourceManager
Config() dubbo_cp.Config
RegistryCenter() dubboRegistry.Registry
@@ -72,6 +80,7 @@
EventBus() events.EventBus
DpServer() *dp_server.DpServer
DataplaneCache() *sync.Map
+ CAProvider() secrets.CaProvider
DDSContext() *dds_context.Context
ResourceValidators() ResourceValidators
AppRegCtx() *registry.ApplicationContext
@@ -91,10 +100,14 @@
rom core_manager.ReadOnlyResourceManager
ext context.Context
meshCache *mesh.Cache
+ eac admin.EnvoyAdminClient
lif lookup.LookupIPFunc
configm config_manager.ConfigManager
leadInfo component.LeaderInfo
erf events.EventBus
+ apim api_server.APIManager
+ cam core_ca.Managers
+ acc Access
dsl datasource.Loader
dps *dp_server.DpServer
registryCenter dubboRegistry.Registry
@@ -106,6 +119,7 @@
ddsctx *dds_context.Context
appCtx context.Context
dCache *sync.Map
+ cap secrets.CaProvider
regClient reg_client.RegClient
serviceDiscover dubboRegistry.ServiceDiscovery
appRegCtx *registry.ApplicationContext
@@ -113,6 +127,14 @@
*runtimeInfo
}
+func (b *Builder) Access() Access {
+ return b.acc
+}
+
+func (b *Builder) CAProvider() secrets.CaProvider {
+ return b.cap
+}
+
func BuilderFor(appCtx context.Context, cfg dubbo_cp.Config) (*Builder, error) {
hostname, err := os.Hostname()
if err != nil {
@@ -157,6 +179,16 @@
return b
}
+func (b *Builder) WithEnvoyAdminClient(eac admin.EnvoyAdminClient) *Builder {
+ b.eac = eac
+ return b
+}
+
+func (b *Builder) WithAccess(acc Access) *Builder {
+ b.acc = acc
+ return b
+}
+
func (b *Builder) WithConfigStore(cs core_store.ResourceStore) *Builder {
b.cs = cs
return b
@@ -226,6 +258,15 @@
return b.meshCache
}
+func (b *Builder) WithCAProvider(cap secrets.CaProvider) *Builder {
+ b.cap = cap
+ return b
+}
+
+func (b *Builder) CaManagers() core_ca.Managers {
+ return b.cam
+}
+
func (b *Builder) WithDDSContext(ddsctx *dds_context.Context) *Builder {
b.ddsctx = ddsctx
return b
@@ -241,6 +282,16 @@
return b
}
+func (b *Builder) WithCaManagers(cam core_ca.Managers) *Builder {
+ b.cam = cam
+ return b
+}
+
+func (b *Builder) WithCaManager(name string, cam core_ca.Manager) *Builder {
+ b.cam[name] = cam
+ return b
+}
+
func (b *Builder) WithRegistryCenter(rg dubboRegistry.Registry) *Builder {
b.registryCenter = rg
return b
@@ -271,6 +322,10 @@
return b
}
+func (b *Builder) APIManager() api_server.APIManager {
+ return b.apim
+}
+
func (b *Builder) Build() (Runtime, error) {
if b.cm == nil {
return nil, errors.Errorf("ComponentManager has not been configured")
@@ -302,6 +357,12 @@
if b.meshCache == nil {
return nil, errors.Errorf("MeshCache has not been configured")
}
+ if b.acc == (Access{}) {
+ return nil, errors.Errorf("Access has not been configured")
+ }
+ if b.cap == nil {
+ return nil, errors.Errorf("CaProvider has not been configured")
+ }
return &runtime{
RuntimeInfo: b.runtimeInfo,
@@ -327,6 +388,7 @@
appCtx: b.appCtx,
meshCache: b.meshCache,
regClient: b.regClient,
+ cap: b.cap,
},
Manager: b.cm,
}, nil
@@ -396,6 +458,10 @@
return b.cfg
}
+func (b *Builder) DataSourceLoader() datasource.Loader {
+ return b.dsl
+}
+
func (b *Builder) DDSContext() *dds_context.Context {
return b.ddsctx
}
diff --git a/pkg/core/runtime/runtime.go b/pkg/core/runtime/runtime.go
index 4d380e1..cf1a80a 100644
--- a/pkg/core/runtime/runtime.go
+++ b/pkg/core/runtime/runtime.go
@@ -21,30 +21,47 @@
"context"
"sync"
"time"
-)
-import (
"dubbo.apache.org/dubbo-go/v3/config_center"
"dubbo.apache.org/dubbo-go/v3/metadata/report"
- dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
-)
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ "github.com/apache/dubbo-kubernetes/pkg/multitenant"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
-import (
+ dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
+
dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
"github.com/apache/dubbo-kubernetes/pkg/config/core"
+
config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
"github.com/apache/dubbo-kubernetes/pkg/core/governance"
+
managers_dataplane "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dataplane"
+
managers_mesh "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
"github.com/apache/dubbo-kubernetes/pkg/core/reg_client"
"github.com/apache/dubbo-kubernetes/pkg/core/registry"
+
+ resources_access "github.com/apache/dubbo-kubernetes/pkg/core/resources/access"
+
core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+
core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
+
dp_server "github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
+
+ envoyadmin_access "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/access"
"github.com/apache/dubbo-kubernetes/pkg/events"
+
+ tokens_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/access"
+
+ zone_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone/access"
"github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
+
xds_runtime "github.com/apache/dubbo-kubernetes/pkg/xds/runtime"
)
@@ -85,10 +102,22 @@
AdminRegistry() *registry.Registry
RegClient() reg_client.RegClient
ResourceValidators() ResourceValidators
+ Tenants() multitenant.Tenants
// AppContext returns a context.Context which tracks the lifetime of the apps, it gets cancelled when the app is starting to shutdown.
AppContext() context.Context
XDS() xds_runtime.XDSRuntimeContext
MeshCache() *mesh.Cache
+ CAProvider() secrets.CaProvider
+ CaManagers() ca.Managers
+ Access() Access
+}
+
+type Access struct {
+ ResourceAccess resources_access.ResourceAccess
+ DataplaneTokenAccess tokens_access.DataplaneTokenAccess
+ ZoneTokenAccess zone_access.ZoneTokenAccess
+ EnvoyAdminAccess envoyadmin_access.EnvoyAdminAccess
+ ControlPlaneMetadataAccess access.ControlPlaneMetadataAccess
}
type ResourceValidators struct {
@@ -171,7 +200,27 @@
appCtx context.Context
meshCache *mesh.Cache
regClient reg_client.RegClient
+ tenants multitenant.Tenants
serviceDiscovery dubboRegistry.ServiceDiscovery
+ cap secrets.CaProvider
+ cam ca.Managers
+ acc Access
+}
+
+func (rc *runtimeContext) Access() Access {
+ return rc.acc
+}
+
+func (rc *runtimeContext) CaManagers() ca.Managers {
+ return rc.cam
+}
+
+func (rc *runtimeContext) CAProvider() secrets.CaProvider {
+ return rc.cap
+}
+
+func (b *runtimeContext) Tenants() multitenant.Tenants {
+ return b.tenants
}
func (b *runtimeContext) RegClient() reg_client.RegClient {
diff --git a/pkg/core/secrets/cipher/cipher.go b/pkg/core/secrets/cipher/cipher.go
new file mode 100644
index 0000000..b4b5fd0
--- /dev/null
+++ b/pkg/core/secrets/cipher/cipher.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cipher
+
+type Cipher interface {
+ Encryptor
+ Decryptor
+}
+
+type Encryptor interface {
+ Encrypt([]byte) ([]byte, error)
+}
+
+type Decryptor interface {
+ Decrypt([]byte) ([]byte, error)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/secrets/cipher/none.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/secrets/cipher/none.go
index 987f638..a9f6bd5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/secrets/cipher/none.go
@@ -15,25 +15,20 @@
* limitations under the License.
*/
-package dubbo
+package cipher
-import (
- "archive/zip"
- "bytes"
-)
-
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func None() Cipher {
+ return &none{}
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+var _ Cipher = &none{}
+
+type none struct{}
+
+func (_ none) Encrypt(data []byte) ([]byte, error) {
+ return data, nil
+}
+
+func (_ none) Decrypt(data []byte) ([]byte, error) {
+ return data, nil
+}
diff --git a/pkg/core/secrets/cipher/todo.go b/pkg/core/secrets/cipher/todo.go
new file mode 100644
index 0000000..8438b41
--- /dev/null
+++ b/pkg/core/secrets/cipher/todo.go
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cipher
+
+func TODO() Cipher {
+ return None()
+}
diff --git a/pkg/core/secrets/manager/global_manager.go b/pkg/core/secrets/manager/global_manager.go
new file mode 100644
index 0000000..df6e317
--- /dev/null
+++ b/pkg/core/secrets/manager/global_manager.go
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package manager
+
+import (
+ "context"
+ "time"
+
+ secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ secret_cipher "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+)
+
+func NewGlobalSecretManager(secretStore secret_store.SecretStore, cipher secret_cipher.Cipher) manager.ResourceManager {
+ return &globalSecretManager{
+ secretStore: secretStore,
+ cipher: cipher,
+ }
+}
+
+var _ manager.ResourceManager = &globalSecretManager{}
+
+type globalSecretManager struct {
+ secretStore secret_store.SecretStore
+ cipher secret_cipher.Cipher
+}
+
+func (s *globalSecretManager) Get(ctx context.Context, resource model.Resource, fs ...core_store.GetOptionsFunc) error {
+ secret, ok := resource.(*secret_model.GlobalSecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.secretStore.Get(ctx, secret, fs...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) List(ctx context.Context, resources model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+ secrets, ok := resources.(*secret_model.GlobalSecretResourceList)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.secretStore.List(ctx, secrets, fs...); err != nil {
+ return err
+ }
+ for _, secret := range secrets.Items {
+ if err := s.decrypt(secret); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *globalSecretManager) Create(ctx context.Context, resource model.Resource, fs ...core_store.CreateOptionsFunc) error {
+ secret, ok := resource.(*secret_model.GlobalSecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.encrypt(secret); err != nil {
+ return err
+ }
+ if err := s.secretStore.Create(ctx, secret, append(fs, core_store.CreatedAt(time.Now()))...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) Update(ctx context.Context, resource model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+ secret, ok := resource.(*secret_model.GlobalSecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.encrypt(secret); err != nil {
+ return err
+ }
+ if err := s.secretStore.Update(ctx, secret, append(fs, core_store.ModifiedAt(time.Now()))...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) Delete(ctx context.Context, resource model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+ secret, ok := resource.(*secret_model.GlobalSecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ return s.secretStore.Delete(ctx, secret, fs...)
+}
+
+func (s *globalSecretManager) DeleteAll(ctx context.Context, secrets model.ResourceList, fs ...core_store.DeleteAllOptionsFunc) error {
+ list := &secret_model.SecretResourceList{}
+ if err := s.secretStore.List(ctx, list); err != nil {
+ return err
+ }
+ for _, item := range list.Items {
+ if err := s.Delete(ctx, item, core_store.DeleteBy(model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *globalSecretManager) encrypt(secret *secret_model.GlobalSecretResource) error {
+ if len(secret.Spec.GetData().GetValue()) > 0 {
+ value, err := s.cipher.Encrypt(secret.Spec.Data.Value)
+ if err != nil {
+ return err
+ }
+ secret.Spec.Data.Value = value
+ }
+ return nil
+}
+
+func (s *globalSecretManager) decrypt(secret *secret_model.GlobalSecretResource) error {
+ if len(secret.Spec.GetData().GetValue()) > 0 {
+ value, err := s.cipher.Decrypt(secret.Spec.Data.Value)
+ if err != nil {
+ return err
+ }
+ secret.Spec.Data.Value = value
+ }
+ return nil
+}
diff --git a/pkg/core/secrets/manager/manager.go b/pkg/core/secrets/manager/manager.go
new file mode 100644
index 0000000..cf48727
--- /dev/null
+++ b/pkg/core/secrets/manager/manager.go
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package manager
+
+import (
+ "context"
+ "time"
+
+ "github.com/pkg/errors"
+
+ secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ secret_cipher "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+)
+
+func NewSecretManager(secretStore secret_store.SecretStore, cipher secret_cipher.Cipher, validator SecretValidator, unsafeDelete bool) manager.ResourceManager {
+ return &secretManager{
+ secretStore: secretStore,
+ cipher: cipher,
+ validator: validator,
+ unsafeDelete: unsafeDelete,
+ }
+}
+
+var _ manager.ResourceManager = &secretManager{}
+
+type secretManager struct {
+ secretStore secret_store.SecretStore
+ cipher secret_cipher.Cipher
+ validator SecretValidator
+ unsafeDelete bool
+}
+
+func (s *secretManager) Get(ctx context.Context, resource model.Resource, fs ...core_store.GetOptionsFunc) error {
+ secret, ok := resource.(*secret_model.SecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.secretStore.Get(ctx, secret, fs...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *secretManager) List(ctx context.Context, resources model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+ secrets, ok := resources.(*secret_model.SecretResourceList)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.secretStore.List(ctx, secrets, fs...); err != nil {
+ return err
+ }
+ for _, secret := range secrets.Items {
+ if err := s.decrypt(secret); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *secretManager) Create(ctx context.Context, resource model.Resource, fs ...core_store.CreateOptionsFunc) error {
+ secret, ok := resource.(*secret_model.SecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.encrypt(secret); err != nil {
+ return err
+ }
+ if err := s.secretStore.Create(ctx, secret, append(fs, core_store.CreatedAt(time.Now()))...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *secretManager) Update(ctx context.Context, resource model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+ secret, ok := resource.(*secret_model.SecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ if err := s.encrypt(secret); err != nil {
+ return err
+ }
+ if err := s.secretStore.Update(ctx, secret, append(fs, core_store.ModifiedAt(time.Now()))...); err != nil {
+ return err
+ }
+ return s.decrypt(secret)
+}
+
+func (s *secretManager) Delete(ctx context.Context, resource model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+ secret, ok := resource.(*secret_model.SecretResource)
+ if !ok {
+ return newInvalidTypeError()
+ }
+ opts := core_store.NewDeleteOptions(fs...)
+ if !s.unsafeDelete {
+ if err := s.validator.ValidateDelete(ctx, opts.Name, opts.Mesh); err != nil {
+ return err
+ }
+ }
+ return s.secretStore.Delete(ctx, secret, fs...)
+}
+
+func (s *secretManager) DeleteAll(ctx context.Context, secrets model.ResourceList, fs ...core_store.DeleteAllOptionsFunc) error {
+ list := &secret_model.SecretResourceList{}
+ opts := core_store.NewDeleteAllOptions(fs...)
+ if err := s.secretStore.List(ctx, list, core_store.ListByMesh(opts.Mesh)); err != nil {
+ return err
+ }
+ for _, item := range list.Items {
+ if err := s.Delete(ctx, item, core_store.DeleteBy(model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) {
+ return err
+ }
+ }
+ return nil
+}
+
+func newInvalidTypeError() error {
+ return errors.New("resource has a wrong type")
+}
+
+func (s *secretManager) encrypt(secret *secret_model.SecretResource) error {
+ if len(secret.Spec.GetData().GetValue()) > 0 {
+ value, err := s.cipher.Encrypt(secret.Spec.Data.Value)
+ if err != nil {
+ return err
+ }
+ secret.Spec.Data.Value = value
+ }
+ return nil
+}
+
+func (s *secretManager) decrypt(secret *secret_model.SecretResource) error {
+ if len(secret.Spec.GetData().GetValue()) > 0 {
+ value, err := s.cipher.Decrypt(secret.Spec.Data.Value)
+ if err != nil {
+ return err
+ }
+ secret.Spec.Data.Value = value
+ }
+ return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/secrets/manager/manager_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/secrets/manager/manager_suite_test.go
index 987f638..cd48142 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/secrets/manager/manager_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package manager_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestSecretManager(t *testing.T) {
+ test.RunSpecs(t, "Secret Manager Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/core/secrets/manager/validator.go b/pkg/core/secrets/manager/validator.go
new file mode 100644
index 0000000..cd6d7f8
--- /dev/null
+++ b/pkg/core/secrets/manager/validator.go
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package manager
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/pkg/errors"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+)
+
+var log = core.Log.WithName("secrets").WithName("validator")
+
+type SecretValidator interface {
+ ValidateDelete(ctx context.Context, secretName string, secretMesh string) error
+}
+
+type ValidateDelete func(ctx context.Context, secretName string, secretMesh string) error
+
+func (f ValidateDelete) ValidateDelete(ctx context.Context, secretName string, secretMesh string) error {
+ return f(ctx, secretName, secretMesh)
+}
+
+func NewSecretValidator(caManagers ca.Managers, store core_store.ResourceStore) SecretValidator {
+ return &secretValidator{
+ caManagers: caManagers,
+ store: store,
+ }
+}
+
+type secretValidator struct {
+ caManagers ca.Managers
+ store core_store.ResourceStore
+}
+
+func (s *secretValidator) ValidateDelete(ctx context.Context, name string, mesh string) error {
+ meshRes := core_mesh.NewMeshResource()
+ err := s.store.Get(ctx, meshRes, core_store.GetByKey(mesh, model.NoMesh))
+ if err != nil {
+ if core_store.IsResourceNotFound(err) {
+ return nil // when Mesh no longer exist we should be able to safely delete a secret because it's not referenced anywhere
+ }
+ return err
+ }
+
+ var verr validators.ValidationError
+ for _, backend := range meshRes.Spec.GetMtls().GetBackends() {
+ used, err := s.secretUsedByMTLSBackend(name, meshRes.GetMeta().GetName(), backend)
+ if err != nil {
+ log.Info("error while checking if secret is used by mTLS backend. Deleting secret anyway", "cause", err)
+ }
+ if used {
+ verr.AddViolation("name", fmt.Sprintf(`The secret %q that you are trying to remove is currently in use in Mesh %q in mTLS backend %q. Please remove the reference from the %q backend before removing the secret.`, name, mesh, backend.Name, backend.Name))
+ }
+ }
+ return verr.OrNil()
+}
+
+func (s *secretValidator) secretUsedByMTLSBackend(name string, mesh string, backend *mesh_proto.CertificateAuthorityBackend) (bool, error) {
+ caManager := s.caManagers[backend.Type]
+ if caManager == nil { // this should be caught earlier by validator
+ return false, errors.Errorf("manager of type %q does not exist", backend.Type)
+ }
+ secrets, err := caManager.UsedSecrets(mesh, backend)
+ if err != nil {
+ return false, errors.Wrapf(err, "could not retrieve secrets in use by backend %q", backend.Name)
+ }
+ for _, secret := range secrets {
+ if secret == name {
+ return true, nil
+ }
+ }
+ return false, nil
+}
diff --git a/pkg/core/secrets/manager/validator_test.go b/pkg/core/secrets/manager/validator_test.go
new file mode 100644
index 0000000..0b96397
--- /dev/null
+++ b/pkg/core/secrets/manager/validator_test.go
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package manager_test
+
+import (
+ "context"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "sigs.k8s.io/yaml"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_datasource "github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+ core_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+ secrets_manager "github.com/apache/dubbo-kubernetes/pkg/core/secrets/manager"
+ secrets_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+ ca_builtin "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin"
+ ca_provided "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+ provided_config "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+ resources_memory "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+ "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Secret Validator", func() {
+ var validator secrets_manager.SecretValidator
+ var resManager core_manager.ResourceManager
+ var caManagers core_ca.Managers
+
+ BeforeEach(func() {
+ memoryStore := resources_memory.NewStore()
+ resManager = core_manager.NewResourceManager(memoryStore)
+ caManagers = core_ca.Managers{}
+ secrets_manager.NewSecretValidator(caManagers, memoryStore)
+ validator = secrets_manager.NewSecretValidator(caManagers, memoryStore)
+ secManager := secrets_manager.NewSecretManager(secrets_store.NewSecretStore(memoryStore), cipher.None(), validator, false)
+
+ caManagers["builtin"] = ca_builtin.NewBuiltinCaManager(secManager)
+ caManagers["provided"] = ca_provided.NewProvidedCaManager(core_datasource.NewDataSourceLoader(secManager))
+ })
+
+ type testCase struct {
+ mesh *core_mesh.MeshResource
+ secretName string
+ expected string
+ }
+ DescribeTable("should validate that secrets are in use",
+ func(given testCase) {
+ // given
+ err := resManager.Create(context.Background(), given.mesh, core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+ Expect(err).ToNot(HaveOccurred())
+ err = core_managers.EnsureCAs(context.Background(), caManagers, given.mesh, model.DefaultMesh)
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ verr := validator.ValidateDelete(context.Background(), given.secretName, "default")
+
+ // then
+ actual, err := yaml.Marshal(verr)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(actual).To(MatchYAML(given.expected))
+ },
+ Entry("when secret is used in builtin CA", testCase{
+ mesh: &core_mesh.MeshResource{
+ Spec: &mesh_proto.Mesh{
+ Mtls: &mesh_proto.Mesh_Mtls{
+ EnabledBackend: "ca-1",
+ Backends: []*mesh_proto.CertificateAuthorityBackend{
+ {
+ Name: "ca-1",
+ Type: "builtin",
+ },
+ },
+ },
+ },
+ },
+ secretName: "default.ca-builtin-cert-ca-1",
+ expected: `
+ violations:
+ - field: name
+ message: The secret "default.ca-builtin-cert-ca-1" that you are trying to remove is currently in use in Mesh "default" in mTLS backend "ca-1". Please remove the reference from the "ca-1" backend before removing the secret.`,
+ }),
+ Entry("when secret is used in provided CA", testCase{
+ mesh: &core_mesh.MeshResource{
+ Spec: &mesh_proto.Mesh{
+ Mtls: &mesh_proto.Mesh_Mtls{
+ EnabledBackend: "ca-2",
+ Backends: []*mesh_proto.CertificateAuthorityBackend{
+ {
+ Name: "ca-2",
+ Type: "provided",
+ Conf: proto.MustToStruct(&provided_config.ProvidedCertificateAuthorityConfig{
+ Cert: &system_proto.DataSource{
+ Type: &system_proto.DataSource_Secret{
+ Secret: "my-ca-cert",
+ },
+ },
+ Key: &system_proto.DataSource{
+ Type: &system_proto.DataSource_Secret{
+ Secret: "my-ca-key",
+ },
+ },
+ }),
+ },
+ },
+ },
+ },
+ },
+ secretName: "my-ca-cert",
+ expected: `
+ violations:
+ - field: name
+ message: The secret "my-ca-cert" that you are trying to remove is currently in use in Mesh "default" in mTLS backend "ca-2". Please remove the reference from the "ca-2" backend before removing the secret.`,
+ }),
+ )
+
+ It("should pass validation of secrets that are not in use", func() {
+ // given
+ err := resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ err = validator.ValidateDelete(context.Background(), "some-not-used-secret", "default")
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("should pass validation of secrets in mesh that non exist", func() {
+ // when
+ err := validator.ValidateDelete(context.Background(), "some-not-used-secret", "non-existing-mesh")
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ })
+})
diff --git a/dubboctl/cmd/generate.go b/pkg/core/secrets/store/adapter.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to pkg/core/secrets/store/adapter.go
index 5c2bc6e..c43421b 100644
--- a/dubboctl/cmd/generate.go
+++ b/pkg/core/secrets/store/adapter.go
@@ -13,18 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package store
import (
- "github.com/spf13/cobra"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
)
-func addGenerate(rootCmd *cobra.Command) {
- generateCmd := &cobra.Command{
- Use: "generate",
- Short: "Generate resources, tokens, etc",
- Long: `Generate resources, tokens, etc.`,
- }
- rootCmd.AddCommand(generateCmd)
- NewGenerateCertificateCmd(generateCmd)
+// todo consider unifing SecretStore with ResourceStore
+func NewSecretStore(resourceStore core_store.ResourceStore) SecretStore {
+ return resourceStore
}
diff --git a/dubboctl/cmd/generate.go b/pkg/core/secrets/store/store.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to pkg/core/secrets/store/store.go
index 5c2bc6e..f9852ef 100644
--- a/dubboctl/cmd/generate.go
+++ b/pkg/core/secrets/store/store.go
@@ -13,18 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package store
import (
- "github.com/spf13/cobra"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
)
-func addGenerate(rootCmd *cobra.Command) {
- generateCmd := &cobra.Command{
- Use: "generate",
- Short: "Generate resources, tokens, etc",
- Long: `Generate resources, tokens, etc.`,
- }
- rootCmd.AddCommand(generateCmd)
- NewGenerateCertificateCmd(generateCmd)
+type SecretStore interface {
+ core_store.ResourceStore
}
diff --git a/pkg/core/tokens/default_signing_key.go b/pkg/core/tokens/default_signing_key.go
new file mode 100644
index 0000000..98e2445
--- /dev/null
+++ b/pkg/core/tokens/default_signing_key.go
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "time"
+
+ "github.com/go-logr/logr"
+ "github.com/pkg/errors"
+ "github.com/sethvargo/go-retry"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ kuma_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+type defaultSigningKeyComponent struct {
+ signingKeyManager SigningKeyManager
+ log logr.Logger
+ ctx context.Context
+ extensions context.Context
+}
+
+var _ component.Component = &defaultSigningKeyComponent{}
+
+func NewDefaultSigningKeyComponent(
+ ctx context.Context,
+ signingKeyManager SigningKeyManager,
+ log logr.Logger,
+ extensions context.Context,
+) component.Component {
+ return &defaultSigningKeyComponent{
+ signingKeyManager: signingKeyManager,
+ log: log,
+ ctx: ctx,
+ extensions: extensions,
+ }
+}
+
+func (d *defaultSigningKeyComponent) Start(stop <-chan struct{}) error {
+ ctx, cancelFn := context.WithCancel(user.Ctx(d.ctx, user.ControlPlane))
+ defer cancelFn()
+ errChan := make(chan error)
+ go func() {
+ defer close(errChan)
+ backoff := retry.WithMaxDuration(10*time.Minute, retry.NewConstant(5*time.Second)) // if after this time we cannot create a resource - something is wrong and we should return an error which will restart CP.
+ err := retry.Do(ctx, backoff, func(ctx context.Context) error {
+ return retry.RetryableError(CreateDefaultSigningKeyIfNotExist(ctx, d.log, d.signingKeyManager, d.extensions)) // retry all errors
+ })
+ if err != nil {
+ // Retry this operation since on Kubernetes, secrets are validated.
+ // This code can execute before the control plane is ready therefore hooks can fail.
+ errChan <- errors.Wrap(err, "could not create the default signing key")
+ }
+ }()
+ select {
+ case <-stop:
+ return nil
+ case err := <-errChan:
+ return err
+ }
+}
+
+func CreateDefaultSigningKeyIfNotExist(
+ ctx context.Context,
+ log logr.Logger,
+ signingKeyManager SigningKeyManager,
+ extensions context.Context,
+) error {
+ logger := kuma_log.AddFieldsFromCtx(log, ctx, extensions)
+ _, _, err := signingKeyManager.GetLatestSigningKey(ctx)
+ if err == nil {
+ logger.V(1).Info("signing key already exists. Skip creating.")
+ return nil
+ }
+ if _, ok := err.(*SigningKeyNotFound); !ok {
+ return err
+ }
+ if err := signingKeyManager.CreateDefaultSigningKey(ctx); err != nil {
+ logger.V(1).Info("could not create signing key", "err", err)
+ return err
+ }
+ logger.Info("default signing key created")
+ return nil
+}
+
+func (d *defaultSigningKeyComponent) NeedLeaderElection() bool {
+ return true
+}
diff --git a/pkg/core/tokens/issuer.go b/pkg/core/tokens/issuer.go
new file mode 100644
index 0000000..3de4303
--- /dev/null
+++ b/pkg/core/tokens/issuer.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/golang-jwt/jwt/v4"
+ "github.com/pkg/errors"
+ "time"
+)
+
+// Issuer generates tokens.
+// Token is a JWT token with claims that is provided by the actual issuer (for example - Dataplane Token Issuer, User Token Issuer).
+// We place "kid" in token, so we don't have to validate the token against every single signing key.
+// Instead, we take "kid" from the token, retrieve signing key and validate only against this key.
+// A new token is always generated by using the latest signing key.
+type Issuer interface {
+ Generate(ctx context.Context, claims Claims, validFor time.Duration) (Token, error)
+}
+
+type jwtTokenIssuer struct {
+ signingKeyManager SigningKeyManager
+}
+
+func NewTokenIssuer(signingKeyAccessor SigningKeyManager) Issuer {
+ return &jwtTokenIssuer{
+ signingKeyManager: signingKeyAccessor,
+ }
+}
+
+var _ Issuer = &jwtTokenIssuer{}
+
+func (j *jwtTokenIssuer) Generate(ctx context.Context, claims Claims, validFor time.Duration) (Token, error) {
+ signingKey, keyID, err := j.signingKeyManager.GetLatestSigningKey(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ now := core.Now()
+ claims.SetRegisteredClaims(jwt.RegisteredClaims{
+ ID: core.NewUUID(),
+ IssuedAt: jwt.NewNumericDate(now),
+ NotBefore: jwt.NewNumericDate(now.Add(time.Minute * -5)), // todo(jakubdyszkiewicz) parametrize via config and go through all clock skews in the project
+ ExpiresAt: jwt.NewNumericDate(now.Add(validFor)),
+ })
+
+ token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
+ token.Header[KeyIDHeader] = keyID
+ tokenString, err := token.SignedString(signingKey)
+ if err != nil {
+ return "", errors.Wrap(err, "could not sign a token")
+ }
+ return tokenString, nil
+}
+
+var IssuerDisabled = errors.New("issuing tokens using the control plane is disabled.")
diff --git a/pkg/core/tokens/keys.go b/pkg/core/tokens/keys.go
new file mode 100644
index 0000000..37cc4e7
--- /dev/null
+++ b/pkg/core/tokens/keys.go
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "os"
+
+ config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+type PublicKey struct {
+ PEM string
+ KID string
+}
+
+func PublicKeyFromConfig(publicKeys []config_types.PublicKey) ([]PublicKey, error) {
+ var keys []PublicKey
+ for _, key := range publicKeys {
+ publicKey, err := configKeyToCoreKey(key)
+ if err != nil {
+ return nil, err
+ }
+ keys = append(keys, publicKey)
+ }
+ return keys, nil
+}
+
+func PublicKeyByMeshFromConfig(publicKeys []config_types.MeshedPublicKey) (map[string][]PublicKey, error) {
+ byMesh := map[string][]PublicKey{}
+ for _, key := range publicKeys {
+ keys, ok := byMesh[key.Mesh]
+ if !ok {
+ keys = []PublicKey{}
+ }
+ publicKey, err := configKeyToCoreKey(key.PublicKey)
+ if err != nil {
+ return nil, err
+ }
+ keys = append(keys, publicKey)
+ byMesh[key.Mesh] = keys
+ }
+ return byMesh, nil
+}
+
+func configKeyToCoreKey(key config_types.PublicKey) (PublicKey, error) {
+ publicKey := PublicKey{
+ KID: key.KID,
+ }
+ if key.KeyFile != "" {
+ content, err := os.ReadFile(key.KeyFile)
+ if err != nil {
+ return PublicKey{}, err
+ }
+ publicKey.PEM = string(content)
+ }
+ if key.Key != "" {
+ publicKey.PEM = key.Key
+ }
+ return publicKey, nil
+}
diff --git a/pkg/core/tokens/revocations.go b/pkg/core/tokens/revocations.go
new file mode 100644
index 0000000..20bb6e7
--- /dev/null
+++ b/pkg/core/tokens/revocations.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "strings"
+
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+)
+
+// Revocations keeps track of revoked tokens.
+// If only one token is compromised, it's more convenient to revoke it instead of rotate signing key and regenerate all tokens.
+// Revocation list is stored as Secret (in case of mesh scoped tokens) or GlobalSecret (global scoped tokens).
+// IDs of token are stored in secret in comma separated format: "id1,id2".
+type Revocations interface {
+ IsRevoked(ctx context.Context, id string) (bool, error)
+}
+
+func NewRevocations(manager manager.ReadOnlyResourceManager, revocationKey core_model.ResourceKey) Revocations {
+ return &secretRevocations{
+ manager: manager,
+ revocationKey: revocationKey,
+ }
+}
+
+type secretRevocations struct {
+ manager manager.ReadOnlyResourceManager
+ revocationKey core_model.ResourceKey
+}
+
+func (s *secretRevocations) IsRevoked(ctx context.Context, id string) (bool, error) {
+ data, err := s.getSecretData(ctx)
+ if err != nil {
+ return false, err
+ }
+ if len(data) == 0 {
+ return false, nil
+ }
+ rawIds := strings.TrimSuffix(string(data), "\n")
+ ids := strings.Split(rawIds, ",")
+ for _, revokedId := range ids {
+ if revokedId == id {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func (s *secretRevocations) getSecretData(ctx context.Context) ([]byte, error) {
+ // Do a list operation instead of get because of the cache in ReadOnlyResourceManager
+ // For the majority of cases, users do not set revocation secret.
+ // We don't cache not found result of get operation, so with many execution to IsRevoked() we would send a lot of requests to a DB.
+ // We could do get requests and preserve separate cache here (taking into account resource not found),
+ // but there is a high chance that SecretResourceList is already in the cache, because of XDS reconciliation, so we can avoid I/O at all.
+ var resources core_model.ResourceList
+ if s.revocationKey.Mesh == "" {
+ resources = &system.GlobalSecretResourceList{}
+ } else {
+ resources = &system.SecretResourceList{}
+ }
+
+ if err := s.manager.List(ctx, resources, core_store.ListByMesh(s.revocationKey.Mesh)); err != nil {
+ return nil, err
+ }
+ for _, res := range resources.GetItems() {
+ if res.GetMeta().GetName() == s.revocationKey.Name {
+ return res.GetSpec().(*system_proto.Secret).GetData().GetValue(), nil
+ }
+ }
+ return nil, nil
+}
diff --git a/pkg/core/tokens/signing_key.go b/pkg/core/tokens/signing_key.go
new file mode 100644
index 0000000..5570b07
--- /dev/null
+++ b/pkg/core/tokens/signing_key.go
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "crypto/rsa"
+ "crypto/x509"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+)
+
+func NewSigningKey() ([]byte, error) {
+ key, err := util_rsa.GenerateKey(util_rsa.DefaultKeySize)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate RSA key")
+ }
+ return util_rsa.FromPrivateKeyToPEMBytes(key)
+}
+
+func SigningKeyResourceKey(signingKeyPrefix string, keyID KeyID, mesh string) model.ResourceKey {
+ name := fmt.Sprintf("%s-%s", signingKeyPrefix, keyID)
+ if keyID == KeyIDFallbackValue { // backwards compatibility with 1.3.x signing keys https://github.com/kumahq/kuma/issues/4006
+ name = signingKeyPrefix
+ }
+ return model.ResourceKey{
+ Name: name,
+ Mesh: mesh,
+ }
+}
+
+type SigningKeyNotFound struct {
+ KeyID KeyID
+ Prefix string
+ Mesh string
+}
+
+func (s *SigningKeyNotFound) Error() string {
+ key := SigningKeyResourceKey(s.Prefix, s.KeyID, s.Mesh)
+ if s.Mesh == "" {
+ return fmt.Sprintf("there is no signing key with KID %s. GlobalSecret of name %q is not found. If signing key was rotated, regenerate the token", s.KeyID, key.Name)
+ } else {
+ return fmt.Sprintf("there is no signing key with KID %s. Secret of name %q in mesh %q is not found. If signing key was rotated, regenerate the token", s.KeyID, key.Name, key.Mesh)
+ }
+}
+
+func IsSigningKeyNotFound(err error) bool {
+ target := &SigningKeyNotFound{}
+ return errors.As(err, &target)
+}
+
+func keyBytesToRsaPrivateKey(keyBytes []byte) (*rsa.PrivateKey, error) {
+ if util_rsa.IsPrivateKeyPEMBytes(keyBytes) {
+ key, err := util_rsa.FromPEMBytesToPrivateKey(keyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+ }
+
+ // support non-PEM RSA key for legacy reasons
+ key, err := x509.ParsePKCS1PrivateKey(keyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+}
+
+func keyBytesToRsaPublicKey(keyBytes []byte) (*rsa.PublicKey, error) {
+ if util_rsa.IsPublicKeyPEMBytes(keyBytes) {
+ key, err := util_rsa.FromPEMBytesToPublicKey(keyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+ }
+
+ // support non-PEM RSA key for legacy reasons
+ key, err := x509.ParsePKCS1PublicKey(keyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+}
+
+func signingKeySerialNumber(secretName string, signingKeyPrefix string) (int, error) {
+ serialNumberStr := strings.ReplaceAll(secretName, signingKeyPrefix+"-", "")
+ serialNumber, err := strconv.Atoi(serialNumberStr)
+ if err != nil {
+ return 0, err
+ }
+ return serialNumber, nil
+}
+
+func getKeyBytes(
+ ctx context.Context,
+ resManager manager.ReadOnlyResourceManager,
+ signingKeyPrefix string,
+ keyID KeyID,
+) ([]byte, error) {
+ resource := system.NewGlobalSecretResource()
+ if err := resManager.Get(ctx, resource, store.GetBy(SigningKeyResourceKey(signingKeyPrefix, keyID, model.NoMesh))); err != nil {
+ if store.IsResourceNotFound(err) {
+ return nil, &SigningKeyNotFound{
+ KeyID: keyID,
+ Prefix: signingKeyPrefix,
+ }
+ }
+
+ return nil, errors.Wrap(err, "could not retrieve signing key")
+ }
+
+ return resource.Spec.GetData().GetValue(), nil
+}
diff --git a/pkg/core/tokens/signing_key_accessor.go b/pkg/core/tokens/signing_key_accessor.go
new file mode 100644
index 0000000..91dd9bf
--- /dev/null
+++ b/pkg/core/tokens/signing_key_accessor.go
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "crypto/rsa"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+)
+
+// SigningKeyAccessor access public part of signing key
+// In the future, we may add offline token generation (kumactl without CP running or external system)
+// In that case, we could provide only public key to the CP via static configuration.
+// So we can easily do this by providing separate implementation for this interface.
+type SigningKeyAccessor interface {
+ GetPublicKey(context.Context, KeyID) (*rsa.PublicKey, error)
+ // GetLegacyKey returns legacy key. In pre 1.4.x version of Kuma, we used symmetric HMAC256 method of signing DP keys.
+ // In that case, we have to retrieve private key even for verification.
+ GetLegacyKey(context.Context, KeyID) ([]byte, error)
+}
+
+type signingKeyAccessor struct {
+ resManager manager.ReadOnlyResourceManager
+ signingKeyPrefix string
+}
+
+var _ SigningKeyAccessor = &signingKeyAccessor{}
+
+func NewSigningKeyAccessor(resManager manager.ReadOnlyResourceManager, signingKeyPrefix string) SigningKeyAccessor {
+ return &signingKeyAccessor{
+ resManager: resManager,
+ signingKeyPrefix: signingKeyPrefix,
+ }
+}
+
+func (s *signingKeyAccessor) GetPublicKey(ctx context.Context, keyID KeyID) (*rsa.PublicKey, error) {
+ keyBytes, err := getKeyBytes(ctx, s.resManager, s.signingKeyPrefix, keyID)
+ if err != nil {
+ return nil, err
+ }
+
+ key, err := keyBytesToRsaPrivateKey(keyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return &key.PublicKey, nil
+}
+
+func (s *signingKeyAccessor) GetLegacyKey(ctx context.Context, keyID KeyID) ([]byte, error) {
+ return getKeyBytes(ctx, s.resManager, s.signingKeyPrefix, keyID)
+}
diff --git a/pkg/core/tokens/signing_key_manager.go b/pkg/core/tokens/signing_key_manager.go
new file mode 100644
index 0000000..7719a19
--- /dev/null
+++ b/pkg/core/tokens/signing_key_manager.go
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "crypto/rsa"
+ "strconv"
+ "strings"
+
+ "github.com/pkg/errors"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+)
+
+const (
+ DefaultKeyID = "1"
+)
+
+// SigningKeyManager manages tokens's signing keys.
+// We can have many signing keys in the system.
+// Example: "user-token-signing-key-1", "user-token-signing-key-2" etc.
+// "user-token-signing-key" has a serial number of 0
+// The latest key is a key with a higher serial number (number at the end of the name)
+type SigningKeyManager interface {
+ GetLatestSigningKey(context.Context) (*rsa.PrivateKey, string, error)
+ CreateDefaultSigningKey(context.Context) error
+ CreateSigningKey(ctx context.Context, keyID KeyID) error
+}
+
+func NewSigningKeyManager(manager manager.ResourceManager, signingKeyPrefix string) SigningKeyManager {
+ return &signingKeyManager{
+ manager: manager,
+ signingKeyPrefix: signingKeyPrefix,
+ }
+}
+
+type signingKeyManager struct {
+ manager manager.ResourceManager
+ signingKeyPrefix string
+}
+
+var _ SigningKeyManager = &signingKeyManager{}
+
+func (s *signingKeyManager) GetLatestSigningKey(ctx context.Context) (*rsa.PrivateKey, string, error) {
+ resources := system.GlobalSecretResourceList{}
+ if err := s.manager.List(ctx, &resources); err != nil {
+ return nil, "", errors.Wrap(err, "could not retrieve signing key from secret manager")
+ }
+ return latestSigningKey(&resources, s.signingKeyPrefix, model.NoMesh)
+}
+
+func latestSigningKey(list model.ResourceList, prefix string, mesh string) (*rsa.PrivateKey, string, error) {
+ var signingKey model.Resource
+ highestSerialNumber := -1
+ for _, resource := range list.GetItems() {
+ if !strings.HasPrefix(resource.GetMeta().GetName(), prefix) {
+ continue
+ }
+ serialNumber, _ := signingKeySerialNumber(resource.GetMeta().GetName(), prefix)
+ if serialNumber > highestSerialNumber {
+ signingKey = resource
+ highestSerialNumber = serialNumber
+ }
+ }
+
+ if signingKey == nil {
+ return nil, "", &SigningKeyNotFound{
+ KeyID: DefaultKeyID,
+ Prefix: prefix,
+ Mesh: mesh,
+ }
+ }
+
+ key, err := keyBytesToRsaPrivateKey(signingKey.GetSpec().(*system_proto.Secret).GetData().GetValue())
+ if err != nil {
+ return nil, "", err
+ }
+
+ return key, strconv.Itoa(highestSerialNumber), nil
+}
+
+func (s *signingKeyManager) CreateDefaultSigningKey(ctx context.Context) error {
+ return s.CreateSigningKey(ctx, DefaultKeyID)
+}
+
+func (s *signingKeyManager) CreateSigningKey(ctx context.Context, keyID KeyID) error {
+ key, err := NewSigningKey()
+ if err != nil {
+ return err
+ }
+
+ secret := system.NewGlobalSecretResource()
+ secret.Spec = &system_proto.Secret{
+ Data: &wrapperspb.BytesValue{
+ Value: key,
+ },
+ }
+ return s.manager.Create(ctx, secret, store.CreateBy(SigningKeyResourceKey(s.signingKeyPrefix, keyID, model.NoMesh)))
+}
diff --git a/pkg/core/tokens/static_signing_key_accessor.go b/pkg/core/tokens/static_signing_key_accessor.go
new file mode 100644
index 0000000..09326a0
--- /dev/null
+++ b/pkg/core/tokens/static_signing_key_accessor.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "crypto/rsa"
+
+ "github.com/pkg/errors"
+
+ util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+)
+
+type staticSigningKeyAccessor struct {
+ keys map[string]*rsa.PublicKey
+}
+
+var _ SigningKeyAccessor = &staticSigningKeyAccessor{}
+
+func NewStaticSigningKeyAccessor(keys []PublicKey) (SigningKeyAccessor, error) {
+ s := &staticSigningKeyAccessor{
+ keys: map[string]*rsa.PublicKey{},
+ }
+ for _, key := range keys {
+ publicKey, err := util_rsa.FromPEMBytesToPublicKey([]byte(key.PEM))
+ if err != nil {
+ return nil, err
+ }
+ s.keys[key.KID] = publicKey
+ }
+ return s, nil
+}
+
+func (s *staticSigningKeyAccessor) GetPublicKey(ctx context.Context, keyID KeyID) (*rsa.PublicKey, error) {
+ key, ok := s.keys[keyID]
+ if !ok {
+ return nil, &SigningKeyNotFound{
+ KeyID: keyID,
+ }
+ }
+ return key, nil
+}
+
+func (s *staticSigningKeyAccessor) GetLegacyKey(context.Context, KeyID) ([]byte, error) {
+ return nil, errors.New("not supported")
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/tokens/token.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/tokens/token.go
index 987f638..8e0a62d 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/tokens/token.go
@@ -15,25 +15,20 @@
* limitations under the License.
*/
-package dubbo
+package tokens
-import (
- "archive/zip"
- "bytes"
-)
+import "github.com/golang-jwt/jwt/v4"
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type Token = string
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type Claims interface {
+ jwt.Claims
+ ID() string
+ SetRegisteredClaims(claims jwt.RegisteredClaims)
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+type KeyID = string
+
+const KeyIDFallbackValue = "0"
+
+const KeyIDHeader = "kid" // standard JWT header that indicates which signing key we should use
diff --git a/pkg/core/tokens/validator.go b/pkg/core/tokens/validator.go
new file mode 100644
index 0000000..7823044
--- /dev/null
+++ b/pkg/core/tokens/validator.go
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "crypto/rsa"
+ errors2 "errors"
+ "fmt"
+
+ "github.com/go-logr/logr"
+ "github.com/golang-jwt/jwt/v4"
+ "github.com/pkg/errors"
+
+ store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+)
+
+type Validator interface {
+ // ParseWithValidation parses token and fills data in provided Claims.
+ ParseWithValidation(ctx context.Context, token Token, claims Claims) error
+}
+
+type jwtTokenValidator struct {
+ keyAccessors []SigningKeyAccessor
+ revocations Revocations
+ storeType store_config.StoreType
+ log logr.Logger
+}
+
+func NewValidator(log logr.Logger, keyAccessors []SigningKeyAccessor, revocations Revocations, storeType store_config.StoreType) Validator {
+ return &jwtTokenValidator{
+ log: log,
+ keyAccessors: keyAccessors,
+ revocations: revocations,
+ storeType: storeType,
+ }
+}
+
+var _ Validator = &jwtTokenValidator{}
+
+func (j *jwtTokenValidator) ParseWithValidation(ctx context.Context, rawToken Token, claims Claims) error {
+ token, err := jwt.ParseWithClaims(rawToken, claims, func(token *jwt.Token) (interface{}, error) {
+ var keyID KeyID
+ kid, exists := token.Header[KeyIDHeader]
+ if !exists {
+ return 0, fmt.Errorf("JWT token must have %s header", KeyIDHeader)
+ } else {
+ keyID = kid.(string)
+ }
+ switch token.Method.Alg() {
+ case jwt.SigningMethodHS256.Name:
+ var key []byte
+ var err error
+ for _, keyAccessor := range j.keyAccessors {
+ key, err = keyAccessor.GetLegacyKey(ctx, KeyIDFallbackValue)
+ if err == nil {
+ return key, nil
+ }
+ }
+ return nil, err
+ case jwt.SigningMethodRS256.Name:
+ var key *rsa.PublicKey
+ var err error
+ for _, keyAccessor := range j.keyAccessors {
+ key, err = keyAccessor.GetPublicKey(ctx, keyID)
+ if err == nil {
+ return key, nil
+ }
+ }
+ return nil, err
+ default:
+ return nil, fmt.Errorf("unsupported token alg %s. Allowed algorithms are %s and %s", token.Method.Alg(), jwt.SigningMethodRS256.Name, jwt.SigningMethodHS256)
+ }
+ })
+ if err != nil {
+ signingKeyError := &SigningKeyNotFound{}
+ if errors2.As(err, &signingKeyError) {
+ return signingKeyError
+ }
+ if j.storeType == store_config.MemoryStore {
+ return errors.Wrap(err, "could not parse token. kuma-cp runs with an in-memory database and its state isn't preserved between restarts."+
+ " Keep in mind that an in-memory database cannot be used with multiple instances of the control plane")
+ }
+ return errors.Wrap(err, "could not parse token")
+ }
+ if !token.Valid {
+ return errors.New("token is not valid")
+ }
+
+ revoked, err := j.revocations.IsRevoked(ctx, claims.ID())
+ if err != nil {
+ return errors.Wrap(err, "could not check if the token is revoked")
+ }
+ if revoked {
+ return errors.New("token is revoked")
+ }
+ return nil
+}
diff --git a/pkg/core/xds/types.go b/pkg/core/xds/types.go
index 4035d13..674f984 100644
--- a/pkg/core/xds/types.go
+++ b/pkg/core/xds/types.go
@@ -19,6 +19,7 @@
import (
"fmt"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
"strings"
)
@@ -53,6 +54,24 @@
}
}
+type MatchType string
+
+type SAN struct {
+ MatchType MatchType
+ Value string
+}
+
+const (
+ SANMatchExact MatchType = "Exact"
+ SANMatchPrefix MatchType = "Prefix"
+)
+
+type TlsVersion int32
+
+type CaSecret struct {
+ PemCerts [][]byte
+}
+
// ServiceName is a convenience type alias to clarify the meaning of string value.
type ServiceName = string
@@ -74,6 +93,10 @@
AllowRenegotiation bool
SkipHostnameVerification bool
ServerName string
+ FallbackToSystemCa bool
+ SANs []SAN
+ MinTlsVersion *TlsVersion
+ MaxTlsVersion *TlsVersion
}
type Locality struct {
@@ -94,6 +117,11 @@
ExternalService *ExternalService
}
+type IdentitySecret struct {
+ PemCerts [][]byte
+ PemKey []byte
+}
+
func (e Endpoint) Address() string {
return fmt.Sprintf("%s:%d", e.Target, e.Port)
}
@@ -115,12 +143,13 @@
// Proxy contains required data for generating XDS config that is specific to a data plane proxy.
// The data that is specific for the whole mesh should go into MeshContext.
type Proxy struct {
- Id ProxyId
- APIVersion APIVersion
- Dataplane *core_mesh.DataplaneResource
- Metadata *DataplaneMetadata
- Routing Routing
- Policies MatchedPolicies
+ Id ProxyId
+ APIVersion APIVersion
+ Dataplane *core_mesh.DataplaneResource
+ Metadata *DataplaneMetadata
+ Routing Routing
+ Policies MatchedPolicies
+ EnvoyAdminMTLSCerts ServerSideMTLSCerts
// SecretsTracker allows us to track when a generator references a secret so
// we can be sure to include only those secrets later on.
@@ -134,6 +163,11 @@
Zone string
}
+type ServerSideMTLSCerts struct {
+ CaPEM []byte
+ ServerPair util_tls.KeyPair
+}
+
type ServerSideTLSCertPaths struct {
CertPath string
KeyPath string
diff --git a/pkg/dds/context/context.go b/pkg/dds/context/context.go
index 9a9da71..fe4e64d 100644
--- a/pkg/dds/context/context.go
+++ b/pkg/dds/context/context.go
@@ -19,6 +19,7 @@
import (
"context"
+ "github.com/apache/dubbo-kubernetes/pkg/dds/service"
"reflect"
)
@@ -64,6 +65,7 @@
ServerStreamInterceptors []grpc.StreamServerInterceptor
ServerUnaryInterceptor []grpc.UnaryServerInterceptor
+ EnvoyAdminRPCs service.EnvoyAdminRPCs
}
func DefaultContext(
diff --git a/pkg/dds/service/envoy_admin_rpcs.go b/pkg/dds/service/envoy_admin_rpcs.go
new file mode 100644
index 0000000..a2a74ff
--- /dev/null
+++ b/pkg/dds/service/envoy_admin_rpcs.go
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package service
+
+import (
+ util_grpc "github.com/apache/dubbo-kubernetes/pkg/util/grpc"
+)
+
+const (
+ ConfigDumpRPC = "XDS Config Dump"
+ StatsRPC = "Stats"
+ ClustersRPC = "Clusters"
+)
+
+type EnvoyAdminRPCs struct {
+ XDSConfigDump util_grpc.ReverseUnaryRPCs
+ Stats util_grpc.ReverseUnaryRPCs
+ Clusters util_grpc.ReverseUnaryRPCs
+}
+
+func NewEnvoyAdminRPCs() EnvoyAdminRPCs {
+ return EnvoyAdminRPCs{
+ XDSConfigDump: util_grpc.NewReverseUnaryRPCs(),
+ Stats: util_grpc.NewReverseUnaryRPCs(),
+ Clusters: util_grpc.NewReverseUnaryRPCs(),
+ }
+}
diff --git a/pkg/dds/service/server.go b/pkg/dds/service/server.go
index f960f51..f465df6 100644
--- a/pkg/dds/service/server.go
+++ b/pkg/dds/service/server.go
@@ -20,6 +20,7 @@
import (
"context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/multitenant"
"time"
)
@@ -52,6 +53,11 @@
InterceptServerStream(stream grpc.ServerStream) error
}
+type TenantZoneClientID struct {
+ TenantID string
+ Zone string
+}
+
type GlobalDDSServiceServer struct {
resManager manager.ResourceManager
instanceID string
@@ -132,3 +138,21 @@
func ZoneClientIDFromCtx(ctx context.Context, zone string) ZoneClientID {
return ZoneClientID{Zone: zone}
}
+
+func (id TenantZoneClientID) String() string {
+ if id.TenantID == "" {
+ return id.Zone
+ }
+ return fmt.Sprintf("%s:%s", id.Zone, id.TenantID)
+}
+
+func TenantZoneClientIDFromCtx(ctx context.Context, zone string) TenantZoneClientID {
+ id := TenantZoneClientID{
+ Zone: zone,
+ }
+ tenantID, ok := multitenant.TenantFromCtx(ctx)
+ if ok {
+ id.TenantID = tenantID
+ }
+ return id
+}
diff --git a/pkg/defaults/components.go b/pkg/defaults/components.go
index f5a1e0c..59b31e4 100644
--- a/pkg/defaults/components.go
+++ b/pkg/defaults/components.go
@@ -19,6 +19,8 @@
import (
"context"
+ config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
"sync"
"time"
)
@@ -37,10 +39,12 @@
core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime"
"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+ "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone"
)
var log = core.Log.WithName("defaults")
+// TODO:not exposed
func Setup(runtime runtime.Runtime) error {
if !runtime.Config().IsFederatedZoneCP() { // Don't run defaults in Zone connected to global (it's done in Global)
defaultsComponent := NewDefaultsComponent(
@@ -49,11 +53,36 @@
runtime.Extensions(),
)
+ zoneSigningKeyManager := tokens.NewSigningKeyManager(runtime.ResourceManager(), zone.SigningKeyPrefix)
+ if err := runtime.Add(tokens.NewDefaultSigningKeyComponent(
+ runtime.AppContext(),
+ zoneSigningKeyManager,
+ log.WithValues("secretPrefix", zone.SigningKeyPrefix),
+ runtime.Extensions(),
+ )); err != nil {
+ return err
+ }
+
if err := runtime.Add(defaultsComponent); err != nil {
return err
}
}
+ if runtime.Config().Mode != config_core.Global { // Envoy Admin CA is not synced in multizone and not needed in Global CP.
+ envoyAdminCaDefault := &EnvoyAdminCaDefaultComponent{
+ ResManager: runtime.ResourceManager(),
+ Extensions: runtime.Extensions(),
+ }
+ zoneDefault := &ZoneDefaultComponent{
+ ResManager: runtime.ResourceManager(),
+ Extensions: runtime.Extensions(),
+ ZoneName: runtime.Config().Multizone.Zone.Name,
+ }
+ if err := runtime.Add(envoyAdminCaDefault, zoneDefault); err != nil {
+ return err
+ }
+ }
+
return nil
}
diff --git a/pkg/defaults/envoy_admin_ca.go b/pkg/defaults/envoy_admin_ca.go
new file mode 100644
index 0000000..6961708
--- /dev/null
+++ b/pkg/defaults/envoy_admin_ca.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package defaults
+
+import (
+ "context"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/sethvargo/go-retry"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+ dubbo_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+type EnvoyAdminCaDefaultComponent struct {
+ ResManager manager.ResourceManager
+ Extensions context.Context
+}
+
+var _ component.Component = &EnvoyAdminCaDefaultComponent{}
+
+func (e *EnvoyAdminCaDefaultComponent) Start(stop <-chan struct{}) error {
+ ctx, cancelFn := context.WithCancel(user.Ctx(context.Background(), user.ControlPlane))
+ defer cancelFn()
+ logger := dubbo_log.AddFieldsFromCtx(log, ctx, e.Extensions)
+ errChan := make(chan error)
+ go func() {
+ errChan <- retry.Do(ctx, retry.WithMaxDuration(10*time.Minute, retry.NewConstant(5*time.Second)), func(ctx context.Context) error {
+ if err := EnsureEnvoyAdminCaExist(ctx, e.ResManager, e.Extensions); err != nil {
+ logger.V(1).Info("could not ensure that Envoy Admin CA exists. Retrying.", "err", err)
+ return retry.RetryableError(err)
+ }
+ return nil
+ })
+ }()
+ select {
+ case <-stop:
+ return nil
+ case err := <-errChan:
+ return err
+ }
+}
+
+func (e EnvoyAdminCaDefaultComponent) NeedLeaderElection() bool {
+ return true
+}
+
+func EnsureEnvoyAdminCaExist(
+ ctx context.Context,
+ resManager manager.ResourceManager,
+ extensions context.Context,
+) error {
+ logger := dubbo_log.AddFieldsFromCtx(log, ctx, extensions)
+ _, err := tls.LoadCA(ctx, resManager)
+ if err == nil {
+ logger.V(1).Info("Envoy Admin CA already exists. Skip creating Envoy Admin CA.")
+ return nil
+ }
+ if !store.IsResourceNotFound(err) {
+ return errors.Wrap(err, "error while loading envoy admin CA")
+ }
+ pair, err := tls.GenerateCA()
+ if err != nil {
+ return errors.Wrap(err, "could not generate envoy admin CA")
+ }
+ if err := tls.CreateCA(ctx, *pair, resManager); err != nil {
+ return errors.Wrap(err, "could not create envoy admin CA")
+ }
+ logger.Info("Envoy Admin CA created")
+ return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/envoy/admin/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/envoy/admin/access/access.go
index 987f638..5d53071 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/envoy/admin/access/access.go
@@ -15,25 +15,17 @@
* limitations under the License.
*/
-package dubbo
+package access
import (
- "archive/zip"
- "bytes"
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+// TODO: inspect admin envoy endpoint
+type EnvoyAdminAccess interface {
+ ValidateViewConfigDump(ctx context.Context, user user.User) error
+ ValidateViewStats(ctx context.Context, user user.User) error
+ ValidateViewClusters(ctx context.Context, user user.User) error
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/envoy/admin/access/static.go b/pkg/envoy/admin/access/static.go
new file mode 100644
index 0000000..949fab0
--- /dev/null
+++ b/pkg/envoy/admin/access/static.go
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticEnvoyAdminAccess struct {
+ configDump accessMaps
+ stats accessMaps
+ clusters accessMaps
+}
+
+type accessMaps struct {
+ usernames map[string]struct{}
+ groups map[string]struct{}
+}
+
+func (am accessMaps) Validate(user user.User) error {
+ return access.Validate(am.usernames, am.groups, user, "envoy proxy info")
+}
+
+var _ EnvoyAdminAccess = &staticEnvoyAdminAccess{}
+
+func NewStaticEnvoyAdminAccess(
+ configDumpCfg config_access.ViewConfigDumpStaticAccessConfig,
+ statsCfg config_access.ViewStatsStaticAccessConfig,
+ clustersCfg config_access.ViewClustersStaticAccessConfig,
+) EnvoyAdminAccess {
+ return &staticEnvoyAdminAccess{
+ configDump: mapAccess(configDumpCfg.Users, configDumpCfg.Groups),
+ stats: mapAccess(statsCfg.Users, statsCfg.Groups),
+ clusters: mapAccess(clustersCfg.Users, clustersCfg.Groups),
+ }
+}
+
+func mapAccess(users []string, groups []string) accessMaps {
+ m := accessMaps{
+ usernames: make(map[string]struct{}, len(users)),
+ groups: make(map[string]struct{}, len(groups)),
+ }
+ for _, usr := range users {
+ m.usernames[usr] = struct{}{}
+ }
+ for _, group := range groups {
+ m.groups[group] = struct{}{}
+ }
+ return m
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewConfigDump(ctx context.Context, user user.User) error {
+ return s.configDump.Validate(user)
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewStats(ctx context.Context, user user.User) error {
+ return s.stats.Validate(user)
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewClusters(ctx context.Context, user user.User) error {
+ return s.clusters.Validate(user)
+}
diff --git a/pkg/envoy/admin/client.go b/pkg/envoy/admin/client.go
new file mode 100644
index 0000000..c78a674
--- /dev/null
+++ b/pkg/envoy/admin/client.go
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package admin
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "time"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+)
+
+type EnvoyAdminClient interface {
+ PostQuit(ctx context.Context, dataplane *core_mesh.DataplaneResource) error
+ ConfigDump(ctx context.Context, proxy core_model.ResourceWithAddress, includeEds bool) ([]byte, error)
+}
+
+type envoyAdminClient struct {
+ rm manager.ResourceManager
+ caManagers ca.Managers
+ defaultAdminPort uint32
+
+ caCertPool *x509.CertPool
+ clientCert *tls.Certificate
+}
+
+func NewEnvoyAdminClient(rm manager.ResourceManager, caManagers ca.Managers, adminPort uint32) EnvoyAdminClient {
+ client := &envoyAdminClient{
+ rm: rm,
+ caManagers: caManagers,
+ defaultAdminPort: adminPort,
+ }
+ return client
+}
+
+func (a *envoyAdminClient) buildHTTPClient(ctx context.Context) (*http.Client, error) {
+ caCertPool, clientCert, err := a.mtlsCerts(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ c := &http.Client{
+ Transport: &http.Transport{
+ DialContext: (&net.Dialer{
+ Timeout: 3 * time.Second,
+ }).DialContext,
+ TLSHandshakeTimeout: 3 * time.Second,
+ TLSClientConfig: &tls.Config{
+ MinVersion: tls.VersionTLS12,
+ RootCAs: &caCertPool,
+ Certificates: []tls.Certificate{clientCert},
+ },
+ },
+ Timeout: 5 * time.Second,
+ }
+ return c, err
+}
+
+func (a *envoyAdminClient) mtlsCerts(ctx context.Context) (x509.CertPool, tls.Certificate, error) {
+ if a.caCertPool == nil {
+ ca, err := envoy_admin_tls.LoadCA(ctx, a.rm)
+ if err != nil {
+ return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not load the CA")
+ }
+ caCertPool := x509.NewCertPool()
+ caCert, err := x509.ParseCertificate(ca.Certificate[0])
+ if err != nil {
+ return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not parse CA")
+ }
+ caCertPool.AddCert(caCert)
+
+ pair, err := envoy_admin_tls.GenerateClientCert(ca)
+ if err != nil {
+ return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not generate a client certificate")
+ }
+ clientCert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM)
+ if err != nil {
+ return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not parse the client certificate")
+ }
+
+ // cache the certs, so we don't have to load the CA and generate them on every single request.
+ // This means that if we want to change Envoy Admin CA, we need to restart all CP instances.
+ a.caCertPool = caCertPool
+ a.clientCert = &clientCert
+ }
+ return *a.caCertPool, *a.clientCert, nil
+}
+
+const (
+ quitquitquit = "quitquitquit"
+)
+
+func (a *envoyAdminClient) PostQuit(ctx context.Context, dataplane *core_mesh.DataplaneResource) error {
+ httpClient, err := a.buildHTTPClient(ctx)
+ if err != nil {
+ return err
+ }
+
+ url := fmt.Sprintf("https://%s/%s", dataplane.AdminAddress(a.defaultAdminPort), quitquitquit)
+ request, err := http.NewRequestWithContext(ctx, "POST", url, nil)
+ if err != nil {
+ return err
+ }
+
+ // Envoy will not send back any response, so do we not check the response
+ response, err := httpClient.Do(request)
+ if errors.Is(err, io.EOF) {
+ return nil // Envoy may not respond correctly for this request because it already started the shut-down process.
+ }
+ if err != nil {
+ return errors.Wrapf(err, "unable to send POST to %s", quitquitquit)
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return errors.Errorf("envoy response [%d %s] [%s]", response.StatusCode, response.Status, response.Body)
+ }
+
+ return nil
+}
+
+func (a *envoyAdminClient) ConfigDump(ctx context.Context, proxy core_model.ResourceWithAddress, includeEds bool) ([]byte, error) {
+ query := url.Values{}
+ if includeEds {
+ query.Add("include_eds", "true")
+ }
+ configDump, err := a.executeRequest(ctx, proxy, "config_dump", query)
+ if err != nil {
+ return nil, err
+ }
+
+ var value []byte
+ if value, err = Sanitize(configDump); err != nil {
+ return nil, err
+ }
+
+ return value, nil
+}
+
+func (a *envoyAdminClient) executeRequest(ctx context.Context, proxy core_model.ResourceWithAddress, path string, query url.Values) ([]byte, error) {
+ var httpClient *http.Client
+ var err error
+ u := &url.URL{}
+
+ switch proxy.(type) {
+ case *core_mesh.DataplaneResource:
+ httpClient, err = a.buildHTTPClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ u.Scheme = "https"
+ case *core_mesh.ZoneIngressResource, *core_mesh.ZoneEgressResource:
+ httpClient, err = a.buildHTTPClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ u.Scheme = "https"
+ default:
+ return nil, errors.New("unsupported proxy type")
+ }
+
+ if host, _, err := net.SplitHostPort(proxy.AdminAddress(a.defaultAdminPort)); err == nil && host == "127.0.0.1" {
+ httpClient = &http.Client{
+ Timeout: 5 * time.Second,
+ }
+ u.Scheme = "http"
+ }
+
+ u.Host = proxy.AdminAddress(a.defaultAdminPort)
+ u.Path = path
+ u.RawQuery = query.Encode()
+ request, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ response, err := httpClient.Do(request)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to send GET to %s", "config_dump")
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return nil, errors.Errorf("envoy response [%d %s] [%s]", response.StatusCode, response.Status, response.Body)
+ }
+
+ resp, err := io.ReadAll(response.Body)
+ if err != nil {
+ return nil, err
+ }
+ return resp, nil
+}
diff --git a/pkg/envoy/admin/sanitize.go b/pkg/envoy/admin/sanitize.go
new file mode 100644
index 0000000..4757267
--- /dev/null
+++ b/pkg/envoy/admin/sanitize.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package admin
+
+import (
+ "strings"
+
+ envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
+
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+func Sanitize(configDump []byte) ([]byte, error) {
+ toReplace := []string{}
+ cd := &envoy_admin_v3.ConfigDump{}
+ if err := util_proto.FromJSON(configDump, cd); err != nil {
+ return nil, err
+ }
+ for _, config := range cd.Configs {
+ if config.MessageIs(&envoy_admin_v3.BootstrapConfigDump{}) {
+ bootstrapConfigDump := &envoy_admin_v3.BootstrapConfigDump{}
+ if err := config.UnmarshalTo(bootstrapConfigDump); err != nil {
+ return nil, err
+ }
+
+ for _, grpcService := range bootstrapConfigDump.GetBootstrap().GetDynamicResources().GetAdsConfig().GetGrpcServices() {
+ for i, initMeta := range grpcService.InitialMetadata {
+ if initMeta.Key == "authorization" {
+ toReplace = append(toReplace, grpcService.InitialMetadata[i].Value, "[redacted]")
+ }
+ }
+ }
+
+ for _, grpcService := range bootstrapConfigDump.GetBootstrap().GetHdsConfig().GetGrpcServices() {
+ for i, initMeta := range grpcService.InitialMetadata {
+ if initMeta.Key == "authorization" {
+ toReplace = append(toReplace, grpcService.InitialMetadata[i].Value, "[redacted]")
+ }
+ }
+ }
+ }
+ }
+ return []byte(strings.NewReplacer(toReplace...).Replace(string(configDump))), nil
+}
diff --git a/pkg/envoy/admin/sanitize_test.go b/pkg/envoy/admin/sanitize_test.go
new file mode 100644
index 0000000..522a125
--- /dev/null
+++ b/pkg/envoy/admin/sanitize_test.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package admin_test
+
+import (
+ "os"
+ "path"
+ "path/filepath"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+ "github.com/apache/dubbo-kubernetes/pkg/test/matchers"
+ _ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+)
+
+var _ = Describe("Sanitize ConfigDump", func() {
+ type testCase struct {
+ configFile string
+ goldenFile string
+ }
+
+ DescribeTable("should redact sensitive information",
+ func(given testCase) {
+ // given
+ rawConfigDump, err := os.ReadFile(filepath.Join("testdata", given.configFile))
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ sanitizedDump, err := admin.Sanitize(rawConfigDump)
+ Expect(err).ToNot(HaveOccurred())
+
+ // then
+ Expect(sanitizedDump).To(matchers.MatchGoldenJSON(path.Join("testdata", given.goldenFile)))
+ },
+ Entry("full config", testCase{
+ configFile: "full_config.json",
+ goldenFile: "golden.full_config.json",
+ }),
+ Entry("no hds", testCase{
+ configFile: "no_hds.json",
+ goldenFile: "golden.no_hds.json",
+ }),
+ )
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/envoy/admin/suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/envoy/admin/suite_test.go
index 987f638..17b0b7a 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/envoy/admin/suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package admin_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestSanitize(t *testing.T) {
+ test.RunSpecs(t, "Sanitize Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/envoy/admin/testdata/full_config.json b/pkg/envoy/admin/testdata/full_config.json
new file mode 100644
index 0000000..b9e9e01
--- /dev/null
+++ b/pkg/envoy/admin/testdata/full_config.json
@@ -0,0 +1,1429 @@
+{
+ "configs": [
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+ "bootstrap": {
+ "node": {
+ "id": "default.backend-1",
+ "cluster": "backend",
+ "metadata": {
+ "dataplane.proxyType": "dataplane",
+ "version": {
+ "envoy": {
+ "version": "1.19.0",
+ "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+ },
+ "dependencies": {},
+ "kumaDp": {
+ "version": "dev-9fd12f2f3",
+ "buildDate": "2022-02-03T17:18:11Z",
+ "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+ "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+ }
+ },
+ "dataplane.dns.port": "15054",
+ "dynamicMetadata": {
+ "version.dependencies.coredns": "1.8.3"
+ },
+ "dataplane.admin.port": "6606",
+ "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+ },
+ "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+ "user_agent_name": "envoy",
+ "user_agent_build_version": {
+ "version": {
+ "major_number": 1,
+ "minor_number": 19
+ },
+ "metadata": {
+ "build.type": "RELEASE",
+ "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+ "revision.status": "Modified",
+ "ssl.version": "BoringSSL"
+ }
+ },
+ "extensions": [
+ {
+ "name": "envoy.filters.dubbo.router",
+ "category": "envoy.dubbo_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.rate_limit",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.router",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "composite-action",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "skip",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "envoy.rate_limit_descriptors.expr",
+ "category": "envoy.rate_limit_descriptors"
+ },
+ {
+ "name": "envoy.matching.common_inputs.environment_variable",
+ "category": "envoy.matching.common_inputs"
+ },
+ {
+ "name": "envoy.compression.brotli.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.compression.gzip.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.access_loggers.file",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.http_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.open_telemetry",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stderr",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stdout",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.tcp_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.wasm",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.file_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.http_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.open_telemetry_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stderr_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stdout_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.tcp_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.wasm_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.dynamic.ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.datadog",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.dynamic_ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.opencensus",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.skywalking",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.xray",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.bootstrap.wasm",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.filters.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.connection_limit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.direct_response",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.dubbo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.kafka_broker",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.local_ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mysql_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.postgres_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rbac",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rocketmq_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_cluster",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.thrift_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.wasm",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.zookeeper_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.custom_header",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.xff",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "request-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "request-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "envoy.filters.udp.dns_filter",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "envoy.filters.udp_listener.udp_proxy",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "dubbo",
+ "category": "envoy.dubbo_proxy.protocols"
+ },
+ {
+ "name": "envoy.ip",
+ "category": "envoy.resolvers"
+ },
+ {
+ "name": "envoy.compression.brotli.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.compression.gzip.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.matching.matchers.consistent_hashing",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "envoy.matching.matchers.ip",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "preserve_case",
+ "category": "envoy.http.stateful_header_formatters"
+ },
+ {
+ "name": "envoy.watchdog.abort_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.watchdog.profile_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.filters.connection_pools.tcp.generic",
+ "category": "envoy.upstreams"
+ },
+ {
+ "name": "envoy.formatter.req_without_query",
+ "category": "envoy.formatter"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "framed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "header",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "unframed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "envoy.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.adaptive_concurrency",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.admission_control",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.alternate_protocols_cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_lambda",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_request_signing",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cdn_loop",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.composite",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.compressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.decompressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamic_forward_proxy",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamo",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_stats",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.header_to_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.jwt_authn",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.local_ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.oauth2",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.on_demand",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.original_src",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.rbac",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.set_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.tap",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.wasm",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.http_dynamo_filter",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.local_rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "match-wrapper",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.extensions.http.cache.simple",
+ "category": "envoy.http.cache"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.previous_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.quic.proof_source.filter_chain",
+ "category": "envoy.quic.proof_source"
+ },
+ {
+ "name": "envoy.retry_priorities.previous_priorities",
+ "category": "envoy.retry_priorities"
+ },
+ {
+ "name": "envoy.wasm.runtime.null",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.wasm.runtime.v8",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.quic.crypto_stream.server.quiche",
+ "category": "envoy.quic.server.crypto_stream"
+ },
+ {
+ "name": "envoy.tls.cert_validator.default",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.tls.cert_validator.spiffe",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.hystrix",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.wasm",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "dubbo.hessian2",
+ "category": "envoy.dubbo_proxy.serializers"
+ },
+ {
+ "name": "envoy.cluster.eds",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.logical_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.original_dst",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.static",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.strict_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.aggregate",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.dynamic_forward_proxy",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.redis",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.health_checkers.redis",
+ "category": "envoy.health_checkers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.upstream_proxy_protocol",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.resource_monitors.fixed_heap",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.resource_monitors.injected_resource",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.grpc_credentials.aws_iam",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.default",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.file_based_metadata",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "default",
+ "category": "envoy.dubbo_proxy.route_matchers"
+ },
+ {
+ "name": "envoy.request_id.uuid",
+ "category": "envoy.request_id"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary/non-strict",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "compact",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "twitter",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_canary_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_host_metadata",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.previous_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "category": "envoy.upstream_options"
+ },
+ {
+ "name": "envoy.upstreams.http.http_protocol_options",
+ "category": "envoy.upstream_options"
+ }
+ ]
+ },
+ "static_resources": {
+ "clusters": [
+ {
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "dynamic_resources": {
+ "lds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "cds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "ads_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ }
+ },
+ "admin": {
+ "access_log_path": "/dev/null",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ },
+ "stats_config": {
+ "stats_tags": [
+ {
+ "tag_name": "name",
+ "regex": "^grpc\\.((.+)\\.)"
+ },
+ {
+ "tag_name": "status",
+ "regex": "^grpc.*streams_closed(_([0-9]+))"
+ },
+ {
+ "tag_name": "kafka_name",
+ "regex": "^kafka(\\.(\\S*[0-9]))\\."
+ },
+ {
+ "tag_name": "kafka_type",
+ "regex": "^kafka\\..*\\.(.*)"
+ },
+ {
+ "tag_name": "worker",
+ "regex": "(worker_([0-9]+)\\.)"
+ },
+ {
+ "tag_name": "listener",
+ "regex": "((.+?)\\.)rbac\\."
+ }
+ ]
+ },
+ "hds_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ },
+ "layered_runtime": {
+ "layers": [
+ {
+ "name": "kuma",
+ "static_layer": {
+ "re2.max_program_size.error_level": "4294967295",
+ "re2.max_program_size.warn_level": 1000,
+ "envoy.restart_features.use_apple_api_for_dns_lookups": false
+ }
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.328Z"
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "static_clusters": [
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.373Z"
+ },
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.394Z"
+ }
+ ],
+ "dynamic_active_clusters": [
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "gateway",
+ "type": "EDS",
+ "eds_cluster_config": {
+ "eds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ }
+ },
+ "connect_timeout": "5s",
+ "circuit_breakers": {
+ "thresholds": [
+ {
+ "max_connections": 1024,
+ "max_pending_requests": 1024,
+ "max_requests": 1024,
+ "max_retries": 3
+ }
+ ]
+ },
+ "outlier_detection": {
+ "enforcing_consecutive_5xx": 0,
+ "enforcing_success_rate": 0,
+ "enforcing_consecutive_gateway_failure": 0,
+ "enforcing_consecutive_local_origin_failure": 0,
+ "enforcing_failure_percentage": 0
+ },
+ "typed_extension_protocol_options": {
+ "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+ "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "explicit_http_config": {
+ "http2_protocol_options": {}
+ }
+ }
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.517Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "kuma:envoy:admin",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "kuma_envoy_admin",
+ "load_assignment": {
+ "cluster_name": "kuma:envoy:admin",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.471Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "localhost:10011",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "localhost_10011",
+ "load_assignment": {
+ "cluster_name": "localhost:10011",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10011
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.492Z"
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "dynamic_listeners": [
+ {
+ "name": "inbound:127.0.0.1:10010",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "inbound:127.0.0.1:10010",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10010
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "localhost_10011",
+ "cluster": "localhost:10011"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "INBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.548Z"
+ }
+ },
+ {
+ "name": "outbound:127.0.0.1:10006",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "outbound:127.0.0.1:10006",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10006
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "gateway",
+ "cluster": "gateway",
+ "max_connect_attempts": 5,
+ "idle_timeout": "3600s"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "OUTBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.550Z"
+ }
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+ "static_secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "secret": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/pkg/envoy/admin/testdata/golden.full_config.json b/pkg/envoy/admin/testdata/golden.full_config.json
new file mode 100644
index 0000000..fcecb52
--- /dev/null
+++ b/pkg/envoy/admin/testdata/golden.full_config.json
@@ -0,0 +1,1429 @@
+{
+ "configs": [
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+ "bootstrap": {
+ "node": {
+ "id": "default.backend-1",
+ "cluster": "backend",
+ "metadata": {
+ "dataplane.proxyType": "dataplane",
+ "version": {
+ "envoy": {
+ "version": "1.19.0",
+ "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+ },
+ "dependencies": {},
+ "kumaDp": {
+ "version": "dev-9fd12f2f3",
+ "buildDate": "2022-02-03T17:18:11Z",
+ "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+ "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+ }
+ },
+ "dataplane.dns.port": "15054",
+ "dynamicMetadata": {
+ "version.dependencies.coredns": "1.8.3"
+ },
+ "dataplane.admin.port": "6606",
+ "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+ },
+ "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+ "user_agent_name": "envoy",
+ "user_agent_build_version": {
+ "version": {
+ "major_number": 1,
+ "minor_number": 19
+ },
+ "metadata": {
+ "build.type": "RELEASE",
+ "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+ "revision.status": "Modified",
+ "ssl.version": "BoringSSL"
+ }
+ },
+ "extensions": [
+ {
+ "name": "envoy.filters.dubbo.router",
+ "category": "envoy.dubbo_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.rate_limit",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.router",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "composite-action",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "skip",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "envoy.rate_limit_descriptors.expr",
+ "category": "envoy.rate_limit_descriptors"
+ },
+ {
+ "name": "envoy.matching.common_inputs.environment_variable",
+ "category": "envoy.matching.common_inputs"
+ },
+ {
+ "name": "envoy.compression.brotli.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.compression.gzip.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.access_loggers.file",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.http_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.open_telemetry",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stderr",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stdout",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.tcp_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.wasm",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.file_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.http_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.open_telemetry_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stderr_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stdout_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.tcp_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.wasm_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.dynamic.ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.datadog",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.dynamic_ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.opencensus",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.skywalking",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.xray",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.bootstrap.wasm",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.filters.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.connection_limit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.direct_response",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.dubbo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.kafka_broker",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.local_ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mysql_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.postgres_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rbac",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rocketmq_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_cluster",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.thrift_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.wasm",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.zookeeper_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.custom_header",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.xff",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "request-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "request-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "envoy.filters.udp.dns_filter",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "envoy.filters.udp_listener.udp_proxy",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "dubbo",
+ "category": "envoy.dubbo_proxy.protocols"
+ },
+ {
+ "name": "envoy.ip",
+ "category": "envoy.resolvers"
+ },
+ {
+ "name": "envoy.compression.brotli.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.compression.gzip.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.matching.matchers.consistent_hashing",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "envoy.matching.matchers.ip",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "preserve_case",
+ "category": "envoy.http.stateful_header_formatters"
+ },
+ {
+ "name": "envoy.watchdog.abort_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.watchdog.profile_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.filters.connection_pools.tcp.generic",
+ "category": "envoy.upstreams"
+ },
+ {
+ "name": "envoy.formatter.req_without_query",
+ "category": "envoy.formatter"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "framed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "header",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "unframed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "envoy.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.adaptive_concurrency",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.admission_control",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.alternate_protocols_cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_lambda",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_request_signing",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cdn_loop",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.composite",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.compressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.decompressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamic_forward_proxy",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamo",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_stats",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.header_to_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.jwt_authn",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.local_ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.oauth2",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.on_demand",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.original_src",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.rbac",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.set_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.tap",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.wasm",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.http_dynamo_filter",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.local_rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "match-wrapper",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.extensions.http.cache.simple",
+ "category": "envoy.http.cache"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.previous_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.quic.proof_source.filter_chain",
+ "category": "envoy.quic.proof_source"
+ },
+ {
+ "name": "envoy.retry_priorities.previous_priorities",
+ "category": "envoy.retry_priorities"
+ },
+ {
+ "name": "envoy.wasm.runtime.null",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.wasm.runtime.v8",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.quic.crypto_stream.server.quiche",
+ "category": "envoy.quic.server.crypto_stream"
+ },
+ {
+ "name": "envoy.tls.cert_validator.default",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.tls.cert_validator.spiffe",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.hystrix",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.wasm",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "dubbo.hessian2",
+ "category": "envoy.dubbo_proxy.serializers"
+ },
+ {
+ "name": "envoy.cluster.eds",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.logical_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.original_dst",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.static",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.strict_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.aggregate",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.dynamic_forward_proxy",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.redis",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.health_checkers.redis",
+ "category": "envoy.health_checkers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.upstream_proxy_protocol",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.resource_monitors.fixed_heap",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.resource_monitors.injected_resource",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.grpc_credentials.aws_iam",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.default",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.file_based_metadata",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "default",
+ "category": "envoy.dubbo_proxy.route_matchers"
+ },
+ {
+ "name": "envoy.request_id.uuid",
+ "category": "envoy.request_id"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary/non-strict",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "compact",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "twitter",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_canary_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_host_metadata",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.previous_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "category": "envoy.upstream_options"
+ },
+ {
+ "name": "envoy.upstreams.http.http_protocol_options",
+ "category": "envoy.upstream_options"
+ }
+ ]
+ },
+ "static_resources": {
+ "clusters": [
+ {
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "dynamic_resources": {
+ "lds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "cds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "ads_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "[redacted]"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ }
+ },
+ "admin": {
+ "access_log_path": "/dev/null",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ },
+ "stats_config": {
+ "stats_tags": [
+ {
+ "tag_name": "name",
+ "regex": "^grpc\\.((.+)\\.)"
+ },
+ {
+ "tag_name": "status",
+ "regex": "^grpc.*streams_closed(_([0-9]+))"
+ },
+ {
+ "tag_name": "kafka_name",
+ "regex": "^kafka(\\.(\\S*[0-9]))\\."
+ },
+ {
+ "tag_name": "kafka_type",
+ "regex": "^kafka\\..*\\.(.*)"
+ },
+ {
+ "tag_name": "worker",
+ "regex": "(worker_([0-9]+)\\.)"
+ },
+ {
+ "tag_name": "listener",
+ "regex": "((.+?)\\.)rbac\\."
+ }
+ ]
+ },
+ "hds_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "[redacted]"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ },
+ "layered_runtime": {
+ "layers": [
+ {
+ "name": "kuma",
+ "static_layer": {
+ "re2.max_program_size.error_level": "4294967295",
+ "re2.max_program_size.warn_level": 1000,
+ "envoy.restart_features.use_apple_api_for_dns_lookups": false
+ }
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.328Z"
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "static_clusters": [
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.373Z"
+ },
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.394Z"
+ }
+ ],
+ "dynamic_active_clusters": [
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "gateway",
+ "type": "EDS",
+ "eds_cluster_config": {
+ "eds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ }
+ },
+ "connect_timeout": "5s",
+ "circuit_breakers": {
+ "thresholds": [
+ {
+ "max_connections": 1024,
+ "max_pending_requests": 1024,
+ "max_requests": 1024,
+ "max_retries": 3
+ }
+ ]
+ },
+ "outlier_detection": {
+ "enforcing_consecutive_5xx": 0,
+ "enforcing_success_rate": 0,
+ "enforcing_consecutive_gateway_failure": 0,
+ "enforcing_consecutive_local_origin_failure": 0,
+ "enforcing_failure_percentage": 0
+ },
+ "typed_extension_protocol_options": {
+ "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+ "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "explicit_http_config": {
+ "http2_protocol_options": {}
+ }
+ }
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.517Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "kuma:envoy:admin",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "kuma_envoy_admin",
+ "load_assignment": {
+ "cluster_name": "kuma:envoy:admin",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.471Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "localhost:10011",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "localhost_10011",
+ "load_assignment": {
+ "cluster_name": "localhost:10011",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10011
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.492Z"
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "dynamic_listeners": [
+ {
+ "name": "inbound:127.0.0.1:10010",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "inbound:127.0.0.1:10010",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10010
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "localhost_10011",
+ "cluster": "localhost:10011"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "INBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.548Z"
+ }
+ },
+ {
+ "name": "outbound:127.0.0.1:10006",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "outbound:127.0.0.1:10006",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10006
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "gateway",
+ "cluster": "gateway",
+ "max_connect_attempts": 5,
+ "idle_timeout": "3600s"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "OUTBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.550Z"
+ }
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+ "static_secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "secret": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/pkg/envoy/admin/testdata/golden.no_hds.json b/pkg/envoy/admin/testdata/golden.no_hds.json
new file mode 100644
index 0000000..3a957ec
--- /dev/null
+++ b/pkg/envoy/admin/testdata/golden.no_hds.json
@@ -0,0 +1,1411 @@
+{
+ "configs": [
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+ "bootstrap": {
+ "node": {
+ "id": "default.backend-1",
+ "cluster": "backend",
+ "metadata": {
+ "dataplane.proxyType": "dataplane",
+ "version": {
+ "envoy": {
+ "version": "1.19.0",
+ "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+ },
+ "dependencies": {},
+ "kumaDp": {
+ "version": "dev-9fd12f2f3",
+ "buildDate": "2022-02-03T17:18:11Z",
+ "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+ "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+ }
+ },
+ "dataplane.dns.port": "15054",
+ "dynamicMetadata": {
+ "version.dependencies.coredns": "1.8.3"
+ },
+ "dataplane.admin.port": "6606",
+ "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+ },
+ "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+ "user_agent_name": "envoy",
+ "user_agent_build_version": {
+ "version": {
+ "major_number": 1,
+ "minor_number": 19
+ },
+ "metadata": {
+ "build.type": "RELEASE",
+ "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+ "revision.status": "Modified",
+ "ssl.version": "BoringSSL"
+ }
+ },
+ "extensions": [
+ {
+ "name": "envoy.filters.dubbo.router",
+ "category": "envoy.dubbo_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.rate_limit",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.router",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "composite-action",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "skip",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "envoy.rate_limit_descriptors.expr",
+ "category": "envoy.rate_limit_descriptors"
+ },
+ {
+ "name": "envoy.matching.common_inputs.environment_variable",
+ "category": "envoy.matching.common_inputs"
+ },
+ {
+ "name": "envoy.compression.brotli.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.compression.gzip.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.access_loggers.file",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.http_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.open_telemetry",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stderr",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stdout",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.tcp_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.wasm",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.file_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.http_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.open_telemetry_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stderr_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stdout_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.tcp_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.wasm_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.dynamic.ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.datadog",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.dynamic_ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.opencensus",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.skywalking",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.xray",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.bootstrap.wasm",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.filters.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.connection_limit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.direct_response",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.dubbo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.kafka_broker",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.local_ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mysql_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.postgres_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rbac",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rocketmq_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_cluster",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.thrift_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.wasm",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.zookeeper_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.custom_header",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.xff",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "request-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "request-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "envoy.filters.udp.dns_filter",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "envoy.filters.udp_listener.udp_proxy",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "dubbo",
+ "category": "envoy.dubbo_proxy.protocols"
+ },
+ {
+ "name": "envoy.ip",
+ "category": "envoy.resolvers"
+ },
+ {
+ "name": "envoy.compression.brotli.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.compression.gzip.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.matching.matchers.consistent_hashing",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "envoy.matching.matchers.ip",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "preserve_case",
+ "category": "envoy.http.stateful_header_formatters"
+ },
+ {
+ "name": "envoy.watchdog.abort_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.watchdog.profile_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.filters.connection_pools.tcp.generic",
+ "category": "envoy.upstreams"
+ },
+ {
+ "name": "envoy.formatter.req_without_query",
+ "category": "envoy.formatter"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "framed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "header",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "unframed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "envoy.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.adaptive_concurrency",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.admission_control",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.alternate_protocols_cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_lambda",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_request_signing",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cdn_loop",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.composite",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.compressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.decompressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamic_forward_proxy",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamo",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_stats",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.header_to_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.jwt_authn",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.local_ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.oauth2",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.on_demand",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.original_src",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.rbac",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.set_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.tap",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.wasm",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.http_dynamo_filter",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.local_rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "match-wrapper",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.extensions.http.cache.simple",
+ "category": "envoy.http.cache"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.previous_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.quic.proof_source.filter_chain",
+ "category": "envoy.quic.proof_source"
+ },
+ {
+ "name": "envoy.retry_priorities.previous_priorities",
+ "category": "envoy.retry_priorities"
+ },
+ {
+ "name": "envoy.wasm.runtime.null",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.wasm.runtime.v8",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.quic.crypto_stream.server.quiche",
+ "category": "envoy.quic.server.crypto_stream"
+ },
+ {
+ "name": "envoy.tls.cert_validator.default",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.tls.cert_validator.spiffe",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.hystrix",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.wasm",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "dubbo.hessian2",
+ "category": "envoy.dubbo_proxy.serializers"
+ },
+ {
+ "name": "envoy.cluster.eds",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.logical_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.original_dst",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.static",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.strict_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.aggregate",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.dynamic_forward_proxy",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.redis",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.health_checkers.redis",
+ "category": "envoy.health_checkers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.upstream_proxy_protocol",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.resource_monitors.fixed_heap",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.resource_monitors.injected_resource",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.grpc_credentials.aws_iam",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.default",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.file_based_metadata",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "default",
+ "category": "envoy.dubbo_proxy.route_matchers"
+ },
+ {
+ "name": "envoy.request_id.uuid",
+ "category": "envoy.request_id"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary/non-strict",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "compact",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "twitter",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_canary_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_host_metadata",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.previous_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "category": "envoy.upstream_options"
+ },
+ {
+ "name": "envoy.upstreams.http.http_protocol_options",
+ "category": "envoy.upstream_options"
+ }
+ ]
+ },
+ "static_resources": {
+ "clusters": [
+ {
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "dynamic_resources": {
+ "lds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "cds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "ads_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "[redacted]"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ }
+ },
+ "admin": {
+ "access_log_path": "/dev/null",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ },
+ "stats_config": {
+ "stats_tags": [
+ {
+ "tag_name": "name",
+ "regex": "^grpc\\.((.+)\\.)"
+ },
+ {
+ "tag_name": "status",
+ "regex": "^grpc.*streams_closed(_([0-9]+))"
+ },
+ {
+ "tag_name": "kafka_name",
+ "regex": "^kafka(\\.(\\S*[0-9]))\\."
+ },
+ {
+ "tag_name": "kafka_type",
+ "regex": "^kafka\\..*\\.(.*)"
+ },
+ {
+ "tag_name": "worker",
+ "regex": "(worker_([0-9]+)\\.)"
+ },
+ {
+ "tag_name": "listener",
+ "regex": "((.+?)\\.)rbac\\."
+ }
+ ]
+ },
+ "layered_runtime": {
+ "layers": [
+ {
+ "name": "kuma",
+ "static_layer": {
+ "re2.max_program_size.error_level": "4294967295",
+ "re2.max_program_size.warn_level": 1000,
+ "envoy.restart_features.use_apple_api_for_dns_lookups": false
+ }
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.328Z"
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "static_clusters": [
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.373Z"
+ },
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.394Z"
+ }
+ ],
+ "dynamic_active_clusters": [
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "gateway",
+ "type": "EDS",
+ "eds_cluster_config": {
+ "eds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ }
+ },
+ "connect_timeout": "5s",
+ "circuit_breakers": {
+ "thresholds": [
+ {
+ "max_connections": 1024,
+ "max_pending_requests": 1024,
+ "max_requests": 1024,
+ "max_retries": 3
+ }
+ ]
+ },
+ "outlier_detection": {
+ "enforcing_consecutive_5xx": 0,
+ "enforcing_success_rate": 0,
+ "enforcing_consecutive_gateway_failure": 0,
+ "enforcing_consecutive_local_origin_failure": 0,
+ "enforcing_failure_percentage": 0
+ },
+ "typed_extension_protocol_options": {
+ "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+ "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "explicit_http_config": {
+ "http2_protocol_options": {}
+ }
+ }
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.517Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "kuma:envoy:admin",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "kuma_envoy_admin",
+ "load_assignment": {
+ "cluster_name": "kuma:envoy:admin",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.471Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "localhost:10011",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "localhost_10011",
+ "load_assignment": {
+ "cluster_name": "localhost:10011",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10011
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.492Z"
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "dynamic_listeners": [
+ {
+ "name": "inbound:127.0.0.1:10010",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "inbound:127.0.0.1:10010",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10010
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "localhost_10011",
+ "cluster": "localhost:10011"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "INBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.548Z"
+ }
+ },
+ {
+ "name": "outbound:127.0.0.1:10006",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "outbound:127.0.0.1:10006",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10006
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "gateway",
+ "cluster": "gateway",
+ "max_connect_attempts": 5,
+ "idle_timeout": "3600s"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "OUTBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.550Z"
+ }
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+ "static_secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "secret": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/pkg/envoy/admin/testdata/no_hds.json b/pkg/envoy/admin/testdata/no_hds.json
new file mode 100644
index 0000000..a818bb9
--- /dev/null
+++ b/pkg/envoy/admin/testdata/no_hds.json
@@ -0,0 +1,1411 @@
+{
+ "configs": [
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+ "bootstrap": {
+ "node": {
+ "id": "default.backend-1",
+ "cluster": "backend",
+ "metadata": {
+ "dataplane.proxyType": "dataplane",
+ "version": {
+ "envoy": {
+ "version": "1.19.0",
+ "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+ },
+ "dependencies": {},
+ "kumaDp": {
+ "version": "dev-9fd12f2f3",
+ "buildDate": "2022-02-03T17:18:11Z",
+ "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+ "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+ }
+ },
+ "dataplane.dns.port": "15054",
+ "dynamicMetadata": {
+ "version.dependencies.coredns": "1.8.3"
+ },
+ "dataplane.admin.port": "6606",
+ "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+ },
+ "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+ "user_agent_name": "envoy",
+ "user_agent_build_version": {
+ "version": {
+ "major_number": 1,
+ "minor_number": 19
+ },
+ "metadata": {
+ "build.type": "RELEASE",
+ "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+ "revision.status": "Modified",
+ "ssl.version": "BoringSSL"
+ }
+ },
+ "extensions": [
+ {
+ "name": "envoy.filters.dubbo.router",
+ "category": "envoy.dubbo_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.rate_limit",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "envoy.filters.thrift.router",
+ "category": "envoy.thrift_proxy.filters"
+ },
+ {
+ "name": "composite-action",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "skip",
+ "category": "envoy.matching.action"
+ },
+ {
+ "name": "envoy.rate_limit_descriptors.expr",
+ "category": "envoy.rate_limit_descriptors"
+ },
+ {
+ "name": "envoy.matching.common_inputs.environment_variable",
+ "category": "envoy.matching.common_inputs"
+ },
+ {
+ "name": "envoy.compression.brotli.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.compression.gzip.decompressor",
+ "category": "envoy.compression.decompressor"
+ },
+ {
+ "name": "envoy.access_loggers.file",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.http_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.open_telemetry",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stderr",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.stdout",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.tcp_grpc",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.access_loggers.wasm",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.file_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.http_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.open_telemetry_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stderr_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.stdout_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.tcp_grpc_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.wasm_access_log",
+ "category": "envoy.access_loggers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.downstream"
+ },
+ {
+ "name": "envoy.dynamic.ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.datadog",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.dynamic_ot",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.lightstep",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.opencensus",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.skywalking",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.xray",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.tracers.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.zipkin",
+ "category": "envoy.tracers"
+ },
+ {
+ "name": "envoy.bootstrap.wasm",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+ "category": "envoy.bootstrap"
+ },
+ {
+ "name": "envoy.filters.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.filters.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.http_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_dst",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.original_src",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.proxy_protocol",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.listener.tls_inspector",
+ "category": "envoy.filters.listener"
+ },
+ {
+ "name": "envoy.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.client_ssl_auth",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.connection_limit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.direct_response",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.dubbo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.echo",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ext_authz",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.kafka_broker",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.local_ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.mysql_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.postgres_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rbac",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.rocketmq_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_cluster",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.thrift_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.wasm",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.filters.network.zookeeper_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http_connection_manager",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.mongo_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.ratelimit",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.redis_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.tcp_proxy",
+ "category": "envoy.filters.network"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.custom_header",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "envoy.http.original_ip_detection.xff",
+ "category": "envoy.http.original_ip_detection"
+ },
+ {
+ "name": "request-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "request-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-headers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "response-trailers",
+ "category": "envoy.matching.http.input"
+ },
+ {
+ "name": "envoy.filters.udp.dns_filter",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "envoy.filters.udp_listener.udp_proxy",
+ "category": "envoy.filters.udp_listener"
+ },
+ {
+ "name": "dubbo",
+ "category": "envoy.dubbo_proxy.protocols"
+ },
+ {
+ "name": "envoy.ip",
+ "category": "envoy.resolvers"
+ },
+ {
+ "name": "envoy.compression.brotli.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.compression.gzip.compressor",
+ "category": "envoy.compression.compressor"
+ },
+ {
+ "name": "envoy.matching.matchers.consistent_hashing",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "envoy.matching.matchers.ip",
+ "category": "envoy.matching.input_matchers"
+ },
+ {
+ "name": "preserve_case",
+ "category": "envoy.http.stateful_header_formatters"
+ },
+ {
+ "name": "envoy.watchdog.abort_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.watchdog.profile_action",
+ "category": "envoy.guarddog_actions"
+ },
+ {
+ "name": "envoy.filters.connection_pools.tcp.generic",
+ "category": "envoy.upstreams"
+ },
+ {
+ "name": "envoy.formatter.req_without_query",
+ "category": "envoy.formatter"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "framed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "header",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "unframed",
+ "category": "envoy.thrift_proxy.transports"
+ },
+ {
+ "name": "envoy.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.adaptive_concurrency",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.admission_control",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.alternate_protocols_cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_lambda",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.aws_request_signing",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.bandwidth_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.buffer",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cache",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cdn_loop",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.composite",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.compressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.cors",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.csrf",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.decompressor",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamic_forward_proxy",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.dynamo",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_authz",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ext_proc",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.fault",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_stats",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.header_to_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.jwt_authn",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.local_ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.oauth2",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.on_demand",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.original_src",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.ratelimit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.rbac",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.set_metadata",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.tap",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.filters.http.wasm",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_http1_bridge",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_json_transcoder",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.grpc_web",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.health_check",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.http_dynamo_filter",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.ip_tagging",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.local_rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.lua",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.rate_limit",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.router",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.squash",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "match-wrapper",
+ "category": "envoy.filters.http"
+ },
+ {
+ "name": "envoy.extensions.http.cache.simple",
+ "category": "envoy.http.cache"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.previous_routes",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+ "category": "envoy.internal_redirect_predicates"
+ },
+ {
+ "name": "envoy.quic.proof_source.filter_chain",
+ "category": "envoy.quic.proof_source"
+ },
+ {
+ "name": "envoy.retry_priorities.previous_priorities",
+ "category": "envoy.retry_priorities"
+ },
+ {
+ "name": "envoy.wasm.runtime.null",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.wasm.runtime.v8",
+ "category": "envoy.wasm.runtime"
+ },
+ {
+ "name": "envoy.quic.crypto_stream.server.quiche",
+ "category": "envoy.quic.server.crypto_stream"
+ },
+ {
+ "name": "envoy.tls.cert_validator.default",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.tls.cert_validator.spiffe",
+ "category": "envoy.tls.cert_validator"
+ },
+ {
+ "name": "envoy.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.dog_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.graphite_statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.hystrix",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.metrics_service",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.stat_sinks.wasm",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "envoy.statsd",
+ "category": "envoy.stats_sinks"
+ },
+ {
+ "name": "dubbo.hessian2",
+ "category": "envoy.dubbo_proxy.serializers"
+ },
+ {
+ "name": "envoy.cluster.eds",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.logical_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.original_dst",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.static",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.cluster.strict_dns",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.aggregate",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.dynamic_forward_proxy",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.clusters.redis",
+ "category": "envoy.clusters"
+ },
+ {
+ "name": "envoy.health_checkers.redis",
+ "category": "envoy.health_checkers"
+ },
+ {
+ "name": "envoy.transport_sockets.alts",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.quic",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tap",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.transport_sockets.upstream_proxy_protocol",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "raw_buffer",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "starttls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "tls",
+ "category": "envoy.transport_sockets.upstream"
+ },
+ {
+ "name": "envoy.resource_monitors.fixed_heap",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.resource_monitors.injected_resource",
+ "category": "envoy.resource_monitors"
+ },
+ {
+ "name": "envoy.grpc_credentials.aws_iam",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.default",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "envoy.grpc_credentials.file_based_metadata",
+ "category": "envoy.grpc_credentials"
+ },
+ {
+ "name": "default",
+ "category": "envoy.dubbo_proxy.route_matchers"
+ },
+ {
+ "name": "envoy.request_id.uuid",
+ "category": "envoy.request_id"
+ },
+ {
+ "name": "auto",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "binary/non-strict",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "compact",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "twitter",
+ "category": "envoy.thrift_proxy.protocols"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_canary_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.omit_host_metadata",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.retry_host_predicates.previous_hosts",
+ "category": "envoy.retry_host_predicates"
+ },
+ {
+ "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "category": "envoy.upstream_options"
+ },
+ {
+ "name": "envoy.upstreams.http.http_protocol_options",
+ "category": "envoy.upstream_options"
+ }
+ ]
+ },
+ "static_resources": {
+ "clusters": [
+ {
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "dynamic_resources": {
+ "lds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "cds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ },
+ "ads_config": {
+ "api_type": "GRPC",
+ "grpc_services": [
+ {
+ "envoy_grpc": {
+ "cluster_name": "ads_cluster"
+ },
+ "initial_metadata": [
+ {
+ "key": "authorization",
+ "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+ }
+ ]
+ }
+ ],
+ "set_node_on_first_message_only": true,
+ "transport_api_version": "V3"
+ }
+ },
+ "admin": {
+ "access_log_path": "/dev/null",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ },
+ "stats_config": {
+ "stats_tags": [
+ {
+ "tag_name": "name",
+ "regex": "^grpc\\.((.+)\\.)"
+ },
+ {
+ "tag_name": "status",
+ "regex": "^grpc.*streams_closed(_([0-9]+))"
+ },
+ {
+ "tag_name": "kafka_name",
+ "regex": "^kafka(\\.(\\S*[0-9]))\\."
+ },
+ {
+ "tag_name": "kafka_type",
+ "regex": "^kafka\\..*\\.(.*)"
+ },
+ {
+ "tag_name": "worker",
+ "regex": "(worker_([0-9]+)\\.)"
+ },
+ {
+ "tag_name": "listener",
+ "regex": "((.+?)\\.)rbac\\."
+ }
+ ]
+ },
+ "layered_runtime": {
+ "layers": [
+ {
+ "name": "kuma",
+ "static_layer": {
+ "re2.max_program_size.error_level": "4294967295",
+ "re2.max_program_size.warn_level": 1000,
+ "envoy.restart_features.use_apple_api_for_dns_lookups": false
+ }
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.328Z"
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "static_clusters": [
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "access_log_sink",
+ "type": "STATIC",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "access_log_sink",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "pipe": {
+ "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.373Z"
+ },
+ {
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+ "name": "ads_cluster",
+ "type": "STRICT_DNS",
+ "connect_timeout": "1s",
+ "http2_protocol_options": {},
+ "transport_socket": {
+ "name": "envoy.transport_sockets.tls",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+ "common_tls_context": {
+ "tls_params": {
+ "tls_minimum_protocol_version": "TLSv1_2"
+ },
+ "validation_context_sds_secret_config": {
+ "name": "cp_validation_ctx"
+ }
+ },
+ "sni": "localhost"
+ }
+ },
+ "upstream_connection_options": {
+ "tcp_keepalive": {
+ "keepalive_probes": 3,
+ "keepalive_time": 10,
+ "keepalive_interval": 10
+ }
+ },
+ "load_assignment": {
+ "cluster_name": "ads_cluster",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "localhost",
+ "port_value": 5678
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:08.394Z"
+ }
+ ],
+ "dynamic_active_clusters": [
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "gateway",
+ "type": "EDS",
+ "eds_cluster_config": {
+ "eds_config": {
+ "ads": {},
+ "resource_api_version": "V3"
+ }
+ },
+ "connect_timeout": "5s",
+ "circuit_breakers": {
+ "thresholds": [
+ {
+ "max_connections": 1024,
+ "max_pending_requests": 1024,
+ "max_requests": 1024,
+ "max_retries": 3
+ }
+ ]
+ },
+ "outlier_detection": {
+ "enforcing_consecutive_5xx": 0,
+ "enforcing_success_rate": 0,
+ "enforcing_consecutive_gateway_failure": 0,
+ "enforcing_consecutive_local_origin_failure": 0,
+ "enforcing_failure_percentage": 0
+ },
+ "typed_extension_protocol_options": {
+ "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+ "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+ "explicit_http_config": {
+ "http2_protocol_options": {}
+ }
+ }
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.517Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "kuma:envoy:admin",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "kuma_envoy_admin",
+ "load_assignment": {
+ "cluster_name": "kuma:envoy:admin",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 6606
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.471Z"
+ },
+ {
+ "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+ "cluster": {
+ "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+ "name": "localhost:10011",
+ "type": "STATIC",
+ "connect_timeout": "10s",
+ "alt_stat_name": "localhost_10011",
+ "load_assignment": {
+ "cluster_name": "localhost:10011",
+ "endpoints": [
+ {
+ "lb_endpoints": [
+ {
+ "endpoint": {
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10011
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "last_updated": "2022-02-03T17:48:09.492Z"
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "dynamic_listeners": [
+ {
+ "name": "inbound:127.0.0.1:10010",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "inbound:127.0.0.1:10010",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10010
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "localhost_10011",
+ "cluster": "localhost:10011"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "INBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.548Z"
+ }
+ },
+ {
+ "name": "outbound:127.0.0.1:10006",
+ "active_state": {
+ "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+ "listener": {
+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+ "name": "outbound:127.0.0.1:10006",
+ "address": {
+ "socket_address": {
+ "address": "127.0.0.1",
+ "port_value": 10006
+ }
+ },
+ "filter_chains": [
+ {
+ "filters": [
+ {
+ "name": "envoy.filters.network.tcp_proxy",
+ "typed_config": {
+ "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+ "stat_prefix": "gateway",
+ "cluster": "gateway",
+ "max_connect_attempts": 5,
+ "idle_timeout": "3600s"
+ }
+ }
+ ]
+ }
+ ],
+ "traffic_direction": "OUTBOUND"
+ },
+ "last_updated": "2022-02-03T17:48:09.550Z"
+ }
+ }
+ ]
+ },
+ {
+ "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+ "static_secrets": [
+ {
+ "name": "cp_validation_ctx",
+ "secret": {
+ "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+ "name": "cp_validation_ctx",
+ "validation_context": {
+ "trusted_ca": {
+ "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+ },
+ "match_subject_alt_names": [
+ {
+ "exact": "localhost"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/pkg/envoy/admin/tls/pki.go b/pkg/envoy/admin/tls/pki.go
new file mode 100644
index 0000000..d997df5
--- /dev/null
+++ b/pkg/envoy/admin/tls/pki.go
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tls
+
+import (
+ "context"
+ "crypto/rsa"
+ tls "crypto/tls"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+const (
+ ClientCertSAN = "dubbo-cp"
+)
+
+var GlobalSecretKey = model.ResourceKey{
+ Name: "envoy-admin-ca",
+}
+
+// GenerateCA generates CA for Envoy Admin communication (CP sending requests to Envoy Admin).
+// While we could reuse CA from enable mTLS backend on a Mesh object there are two problems
+// 1. mTLS on Mesh can be disabled and Envoy Admin communication needs security in place.
+// Otherwise, malicious actor could execute /quitquitquit endpoint and perform DDoS
+// 2. ZoneIngress and ZoneEgress are not scoped to a Mesh.
+//
+// To solve this we need at least self-signed client certificate for the control plane.
+// But we can just as well have a CA and generate client and server certs from it.
+//
+// Rotation: users can change the CA. To do this, they can swap the secret and restart all instances of the CP.
+// Multizone: CA is generated for every zone. There is no need for it to be stable.
+func GenerateCA() (*util_tls.KeyPair, error) {
+ subject := pkix.Name{
+ Organization: []string{"Dubbo"},
+ OrganizationalUnit: []string{"Mesh"},
+ CommonName: "Envoy Admin CA",
+ }
+ return util_tls.GenerateCA(util_tls.DefaultKeyType, subject)
+}
+
+func LoadCA(ctx context.Context, resManager manager.ReadOnlyResourceManager) (tls.Certificate, error) {
+ globalSecret := system.NewGlobalSecretResource()
+ if err := resManager.Get(ctx, globalSecret, store.GetBy(GlobalSecretKey)); err != nil {
+ return tls.Certificate{}, err
+ }
+ bytes := globalSecret.Spec.GetData().GetValue()
+ certBlock, rest := pem.Decode(bytes)
+ keyBlock, _ := pem.Decode(rest)
+ return tls.X509KeyPair(pem.EncodeToMemory(certBlock), pem.EncodeToMemory(keyBlock))
+}
+
+func CreateCA(ctx context.Context, keyPair util_tls.KeyPair, resManager manager.ResourceManager) error {
+ bytes := append(keyPair.CertPEM, keyPair.KeyPEM...)
+ globalSecret := system.NewGlobalSecretResource()
+ globalSecret.Spec.Data = &wrapperspb.BytesValue{
+ Value: bytes,
+ }
+ return resManager.Create(ctx, globalSecret, store.CreateBy(GlobalSecretKey))
+}
+
+func GenerateClientCert(ca tls.Certificate) (util_tls.KeyPair, error) {
+ rootCert, err := x509.ParseCertificate(ca.Certificate[0])
+ if err != nil {
+ return util_tls.KeyPair{}, err
+ }
+ return util_tls.NewCert(*rootCert, ca.PrivateKey.(*rsa.PrivateKey), util_tls.ClientCertType, util_tls.DefaultKeyType, ClientCertSAN)
+}
+
+func GenerateServerCert(ca tls.Certificate, hosts ...string) (util_tls.KeyPair, error) {
+ rootCert, err := x509.ParseCertificate(ca.Certificate[0])
+ if err != nil {
+ return util_tls.KeyPair{}, err
+ }
+ return util_tls.NewCert(*rootCert, ca.PrivateKey.(*rsa.PrivateKey), util_tls.ServerCertType, util_tls.DefaultKeyType, hosts...)
+}
diff --git a/pkg/multitenant/multitenant.go b/pkg/multitenant/multitenant.go
new file mode 100644
index 0000000..e7cdd2e
--- /dev/null
+++ b/pkg/multitenant/multitenant.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package multitenant
+
+import (
+ "context"
+ "errors"
+
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/trace"
+)
+
+const spanAttributeName = "tenantID"
+
+// GlobalTenantID is a unique ID used for storing resources that are not tenant-aware
+var GlobalTenantID = ""
+
+type tenantCtx struct{}
+
+type Tenants interface {
+ // GetID gets id of tenant from context.
+ // Design: why not rely on TenantFromCtx? Different implementations of Tenants can have different error handling.
+ // Some may return error on missing tenant, whereas Kuma never requires tenant set in context.
+ GetID(ctx context.Context) (string, error)
+ GetIDs(ctx context.Context) ([]string, error)
+
+ // SupportsSharding returns true if Tenants implementation supports sharding.
+ // It means that GetIDs will return only subset of tenants.
+ SupportsSharding() bool
+ // IDSupported returns true if given tenant is in the current shard.
+ IDSupported(ctx context.Context, id string) (bool, error)
+}
+
+var SingleTenant = &singleTenant{}
+
+type singleTenant struct{}
+
+func (s singleTenant) GetID(context.Context) (string, error) {
+ return "", nil
+}
+
+func (s singleTenant) GetIDs(context.Context) ([]string, error) {
+ return []string{""}, nil
+}
+
+func (s singleTenant) SupportsSharding() bool {
+ return false
+}
+
+func (s singleTenant) IDSupported(ctx context.Context, id string) (bool, error) {
+ return true, nil
+}
+
+func WithTenant(ctx context.Context, tenantId string) context.Context {
+ if span := trace.SpanFromContext(ctx); span.IsRecording() {
+ span.SetAttributes(attribute.String(spanAttributeName, tenantId))
+ }
+
+ return context.WithValue(ctx, tenantCtx{}, tenantId)
+}
+
+func TenantFromCtx(ctx context.Context) (string, bool) {
+ value, ok := ctx.Value(tenantCtx{}).(string)
+ return value, ok
+}
+
+var TenantMissingErr = errors.New("tenant is missing")
diff --git a/pkg/plugins/authn/api-server/certs/authenticator.go b/pkg/plugins/authn/api-server/certs/authenticator.go
new file mode 100644
index 0000000..02be5e2
--- /dev/null
+++ b/pkg/plugins/authn/api-server/certs/authenticator.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package certs
+
+import (
+ "github.com/emicklei/go-restful/v3"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+func ClientCertAuthenticator(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+ if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
+ request.Request.TLS != nil &&
+ request.Request.TLS.HandshakeComplete &&
+ len(request.Request.TLS.PeerCertificates) > 0 {
+ request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
+ }
+ chain.ProcessFilter(request, response)
+}
diff --git a/pkg/plugins/authn/api-server/certs/plugin.go b/pkg/plugins/authn/api-server/certs/plugin.go
new file mode 100644
index 0000000..a891135
--- /dev/null
+++ b/pkg/plugins/authn/api-server/certs/plugin.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package certs
+
+import (
+ "github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+)
+
+const PluginName = "adminClientCerts"
+
+var log = core.Log.WithName("plugins").WithName("authn").WithName("api-server").WithName("certs")
+
+type plugin struct{}
+
+func init() {
+ plugins.Register(PluginName, &plugin{})
+}
+
+var _ plugins.AuthnAPIServerPlugin = plugin{}
+
+func (c plugin) NewAuthenticator(_ plugins.PluginContext) (authn.Authenticator, error) {
+ log.Info("WARNING: admin client certificates are deprecated. Please migrate to user token as API Server authentication mechanism.")
+ return ClientCertAuthenticator, nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/access/access.go b/pkg/plugins/authn/api-server/tokens/access/access.go
new file mode 100644
index 0000000..aac7bfc
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/access/access.go
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import "github.com/apache/dubbo-kubernetes/pkg/core/user"
+
+type GenerateUserTokenAccess interface {
+ ValidateGenerate(user user.User) error
+}
diff --git a/pkg/plugins/authn/api-server/tokens/access/static.go b/pkg/plugins/authn/api-server/tokens/access/static.go
new file mode 100644
index 0000000..dde40c5
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/access/static.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticGenerateUserTokenAccess struct {
+ usernames map[string]bool
+ groups map[string]bool
+}
+
+var _ GenerateUserTokenAccess = &staticGenerateUserTokenAccess{}
+
+func NewStaticGenerateUserTokenAccess(cfg config_access.GenerateUserTokenStaticAccessConfig) GenerateUserTokenAccess {
+ s := &staticGenerateUserTokenAccess{
+ usernames: map[string]bool{},
+ groups: map[string]bool{},
+ }
+ for _, user := range cfg.Users {
+ s.usernames[user] = true
+ }
+ for _, group := range cfg.Groups {
+ s.groups[group] = true
+ }
+ return s
+}
+
+func (s *staticGenerateUserTokenAccess) ValidateGenerate(user user.User) error {
+ allowed := s.usernames[user.Name]
+ for _, group := range user.Groups {
+ if s.groups[group] {
+ allowed = true
+ }
+ }
+ if !allowed {
+ return &access.AccessDeniedError{Reason: "action not allowed"}
+ }
+ return nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go
new file mode 100644
index 0000000..0e27844
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/sethvargo/go-retry"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+ config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+)
+
+var AdminTokenKey = model.ResourceKey{
+ Name: "admin-user-token",
+}
+
+type adminTokenBootstrap struct {
+ issuer issuer.UserTokenIssuer
+ resManager manager.ResourceManager
+ cpCfg dubbo_cp.Config
+}
+
+func NewAdminTokenBootstrap(issuer issuer.UserTokenIssuer, resManager manager.ResourceManager, cpCfg dubbo_cp.Config) component.Component {
+ return &adminTokenBootstrap{
+ issuer: issuer,
+ resManager: resManager,
+ cpCfg: cpCfg,
+ }
+}
+
+func (a *adminTokenBootstrap) Start(stop <-chan struct{}) error {
+ ctx, cancelFn := context.WithCancel(user.Ctx(context.Background(), user.ControlPlane))
+ go func() {
+ if err := a.generateTokenIfNotExist(ctx); err != nil {
+ // just log, do not exist control plane
+ log.Error(err, "could not generate Admin User Token")
+ } else {
+ msg := "bootstrap of Admin User Token is enabled. "
+ if a.cpCfg.DeployMode == config_core.KubernetesMode {
+ msg += fmt.Sprintf("To extract credentials execute 'kubectl get secret %s -n %s --template={{.data.value}} | base64 -d'. ", AdminTokenKey.Name, a.cpCfg.Store.Kubernetes.SystemNamespace)
+ } else {
+ msg += fmt.Sprintf("To extract admin credentials execute 'curl http://localhost:%d/global-secrets/%s | jq -r .data | base64 -d'. ", a.cpCfg.ApiServer.HTTP.Port, AdminTokenKey.Name)
+ }
+ msg += "You configure dubboctl with them 'dubboctl config control-planes add --auth-type=tokens --auth-conf token=YOUR_TOKEN'." +
+ " To disable bootstrap of Admin User Token set dubbo_API_SERVER_AUTHN_TOKENS_BOOTSTRAP_ADMIN_TOKEN to false."
+ log.Info(msg)
+ }
+ }()
+ <-stop
+ cancelFn()
+ return nil
+}
+
+func (a *adminTokenBootstrap) generateTokenIfNotExist(ctx context.Context) error {
+ secret := system.NewGlobalSecretResource()
+ err := a.resManager.Get(ctx, secret, core_store.GetBy(AdminTokenKey))
+ if err == nil {
+ return nil // already exists
+ }
+ if !core_store.IsResourceNotFound(err) {
+ return errors.Wrap(err, "could not check if token exist")
+ }
+
+ log.Info("Admin User Token not found. Generating.")
+ token, err := a.generateAdminToken(ctx)
+ if err != nil {
+ return errors.Wrap(err, "could not generate admin token")
+ }
+
+ log.Info("saving generated Admin User Token", "globalSecretName", AdminTokenKey.Name)
+ secret.Spec.Data = &wrapperspb.BytesValue{
+ Value: []byte(token),
+ }
+ if err := a.resManager.Create(ctx, secret, core_store.CreateBy(AdminTokenKey)); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (a *adminTokenBootstrap) generateAdminToken(ctx context.Context) (string, error) {
+ // we need retries because signing key may not be available yet
+ var token string
+ err := retry.Do(ctx, retry.WithMaxDuration(10*time.Minute, retry.NewConstant(time.Second)), func(ctx context.Context) error {
+ t, err := a.issuer.Generate(ctx, user.Admin, 24*365*10*time.Hour)
+ if err != nil {
+ return retry.RetryableError(err)
+ }
+ token = t
+ return nil
+ })
+ return token, err
+}
+
+func (a *adminTokenBootstrap) NeedLeaderElection() bool {
+ return true
+}
+
+var _ component.Component = &adminTokenBootstrap{}
diff --git a/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go
new file mode 100644
index 0000000..5d30733
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens_test
+
+import (
+ "context"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ kuma_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+ store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+)
+
+var _ = Describe("Admin Token Bootstrap", func() {
+ It("should bootstrap admin token", func() {
+ // given
+ ctx := context.Background()
+ resManager := manager.NewResourceManager(memory.NewStore())
+ signingKeyManager := core_tokens.NewSigningKeyManager(resManager, issuer.UserTokenSigningKeyPrefix)
+ tokenIssuer := issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+ tokenValidator := issuer.NewUserTokenValidator(
+ core_tokens.NewValidator(
+ core.Log.WithName("test"),
+ []core_tokens.SigningKeyAccessor{
+ core_tokens.NewSigningKeyAccessor(resManager, issuer.UserTokenSigningKeyPrefix),
+ },
+ core_tokens.NewRevocations(resManager, issuer.UserTokenRevocationsGlobalSecretKey),
+ store_config.MemoryStore,
+ ),
+ )
+
+ component := tokens.NewAdminTokenBootstrap(tokenIssuer, resManager, kuma_cp.DefaultConfig())
+ err := signingKeyManager.CreateDefaultSigningKey(ctx)
+ Expect(err).ToNot(HaveOccurred())
+ stopCh := make(chan struct{})
+ defer close(stopCh)
+
+ // when
+ go func() {
+ _ = component.Start(stopCh) // it never returns an error
+ }()
+
+ // then token is created
+ Eventually(func(g Gomega) {
+ globalSecret := system.NewGlobalSecretResource()
+ err = resManager.Get(context.Background(), globalSecret, core_store.GetBy(tokens.AdminTokenKey))
+ g.Expect(err).ToNot(HaveOccurred())
+ user, err := tokenValidator.Validate(ctx, string(globalSecret.Spec.Data.Value))
+ g.Expect(err).ToNot(HaveOccurred())
+ g.Expect(user.Name).To(Equal("mesh-system:admin"))
+ g.Expect(user.Groups).To(Equal([]string{"mesh-system:admin"}))
+ }).Should(Succeed())
+ })
+})
diff --git a/pkg/plugins/authn/api-server/tokens/authenticator.go b/pkg/plugins/authn/api-server/tokens/authenticator.go
new file mode 100644
index 0000000..c7beac2
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/authenticator.go
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "strings"
+
+ "github.com/emicklei/go-restful/v3"
+
+ "github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ rest_errors "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+)
+
+const bearerPrefix = "Bearer "
+
+var log = core.Log.WithName("plugins").WithName("authn").WithName("api-server").WithName("tokens")
+
+func UserTokenAuthenticator(validator issuer.UserTokenValidator) authn.Authenticator {
+ return func(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+ if authn.SkipAuth(request) {
+ chain.ProcessFilter(request, response)
+ return
+ }
+ authnHeader := request.Request.Header.Get("authorization")
+ if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
+ authnHeader != "" &&
+ strings.HasPrefix(authnHeader, bearerPrefix) {
+ token := strings.TrimPrefix(authnHeader, bearerPrefix)
+ u, err := validator.Validate(request.Request.Context(), token)
+ if err != nil {
+ rest_errors.HandleError(request.Request.Context(), response, &rest_errors.Unauthenticated{}, "Invalid authentication data")
+ log.Info("authentication rejected", "reason", err.Error())
+ return
+ }
+ request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), u.Authenticated()))
+ }
+ chain.ProcessFilter(request, response)
+ }
+}
diff --git a/pkg/plugins/authn/api-server/tokens/cli/plugin.go b/pkg/plugins/authn/api-server/tokens/cli/plugin.go
new file mode 100644
index 0000000..35eb758
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/cli/plugin.go
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cli
+
+import (
+ "net/http"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api"
+ util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+const (
+ AuthType = "tokens"
+ TokenKey = "token"
+)
+
+type TokenAuthnPlugin struct{}
+
+var _ api.AuthnPlugin = &TokenAuthnPlugin{}
+
+func (t *TokenAuthnPlugin) Validate(authConf map[string]string) error {
+ if authConf[TokenKey] == "" {
+ return errors.New("provide token=YOUR_TOKEN")
+ }
+ return nil
+}
+
+func (t *TokenAuthnPlugin) DecorateClient(delegate util_http.Client, authConf map[string]string) (util_http.Client, error) {
+ return util_http.ClientFunc(func(req *http.Request) (*http.Response, error) {
+ req.Header.Set("authorization", "Bearer "+authConf[TokenKey])
+ return delegate.Do(req)
+ }), nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/issuer.go b/pkg/plugins/authn/api-server/tokens/issuer/issuer.go
new file mode 100644
index 0000000..41eeb3f
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/issuer.go
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package issuer
+
+import (
+ "context"
+ "time"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type UserTokenIssuer interface {
+ Generate(ctx context.Context, identity user.User, validFor time.Duration) (tokens.Token, error)
+}
+
+type jwtTokenIssuer struct {
+ issuer tokens.Issuer
+}
+
+func NewUserTokenIssuer(issuer tokens.Issuer) UserTokenIssuer {
+ return &jwtTokenIssuer{
+ issuer: issuer,
+ }
+}
+
+var _ UserTokenIssuer = &jwtTokenIssuer{}
+
+func (j *jwtTokenIssuer) Generate(ctx context.Context, identity user.User, validFor time.Duration) (tokens.Token, error) {
+ c := UserClaims{
+ User: identity,
+ }
+ return j.issuer.Generate(ctx, &c, validFor)
+}
+
+type DisabledIssuer struct{}
+
+func (d DisabledIssuer) Generate(context.Context, user.User, time.Duration) (tokens.Token, error) {
+ return "", tokens.IssuerDisabled
+}
+
+var _ UserTokenIssuer = &DisabledIssuer{}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/token.go b/pkg/plugins/authn/api-server/tokens/issuer/token.go
new file mode 100644
index 0000000..329b53a
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/token.go
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package issuer
+
+import (
+ "github.com/golang-jwt/jwt/v4"
+
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+const UserTokenSigningKeyPrefix = "user-token-signing-key"
+
+var UserTokenRevocationsGlobalSecretKey = core_model.ResourceKey{
+ Name: "user-token-revocations",
+ Mesh: core_model.NoMesh,
+}
+
+type UserClaims struct {
+ user.User
+ jwt.RegisteredClaims
+}
+
+var _ tokens.Claims = &UserClaims{}
+
+func (c *UserClaims) ID() string {
+ return c.RegisteredClaims.ID
+}
+
+func (c *UserClaims) SetRegisteredClaims(claims jwt.RegisteredClaims) {
+ c.RegisteredClaims = claims
+}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/validator.go b/pkg/plugins/authn/api-server/tokens/issuer/validator.go
new file mode 100644
index 0000000..5f9479a
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/validator.go
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package issuer
+
+import (
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type UserTokenValidator interface {
+ Validate(ctx context.Context, token tokens.Token) (user.User, error)
+}
+
+func NewUserTokenValidator(validator tokens.Validator) UserTokenValidator {
+ return &jwtTokenValidator{
+ validator: validator,
+ }
+}
+
+type jwtTokenValidator struct {
+ validator tokens.Validator
+}
+
+func (j *jwtTokenValidator) Validate(ctx context.Context, rawToken tokens.Token) (user.User, error) {
+ claims := &UserClaims{}
+ if err := j.validator.ParseWithValidation(ctx, rawToken, claims); err != nil {
+ return user.User{}, err
+ }
+ return claims.User, nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/plugin.go b/pkg/plugins/authn/api-server/tokens/plugin.go
new file mode 100644
index 0000000..a849226
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/plugin.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ go_context "context"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+ core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/access"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/server"
+)
+
+const PluginName = "tokens"
+
+type plugin struct {
+ // TODO: properly run AfterBootstrap - https://github.com/apache/dubbo-kubernetes/issues/6607
+ isInitialised bool
+}
+
+var (
+ _ plugins.AuthnAPIServerPlugin = &plugin{}
+ _ plugins.BootstrapPlugin = &plugin{}
+)
+
+// We declare AccessStrategies and not into Runtime because it's a plugin.
+var AccessStrategies = map[string]func(*plugins.MutablePluginContext) access.GenerateUserTokenAccess{
+ config_access.StaticType: func(context *plugins.MutablePluginContext) access.GenerateUserTokenAccess {
+ return access.NewStaticGenerateUserTokenAccess(context.Config().Access.Static.GenerateUserToken)
+ },
+}
+
+var NewUserTokenIssuer = func(signingKeyManager core_tokens.SigningKeyManager) issuer.UserTokenIssuer {
+ return issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+}
+
+func init() {
+ plugins.Register(PluginName, &plugin{})
+}
+
+func (c *plugin) NewAuthenticator(context plugins.PluginContext) (authn.Authenticator, error) {
+ publicKeys, err := core_tokens.PublicKeyFromConfig(context.Config().ApiServer.Authn.Tokens.Validator.PublicKeys)
+ if err != nil {
+ return nil, err
+ }
+ staticSigningKeyAccessor, err := core_tokens.NewStaticSigningKeyAccessor(publicKeys)
+ if err != nil {
+ return nil, err
+ }
+ accessors := []core_tokens.SigningKeyAccessor{staticSigningKeyAccessor}
+ if context.Config().ApiServer.Authn.Tokens.Validator.UseSecrets {
+ accessors = append(accessors, core_tokens.NewSigningKeyAccessor(context.ResourceManager(), issuer.UserTokenSigningKeyPrefix))
+ }
+ validator := issuer.NewUserTokenValidator(
+ core_tokens.NewValidator(
+ core.Log.WithName("tokens"),
+ accessors,
+ core_tokens.NewRevocations(context.ResourceManager(), issuer.UserTokenRevocationsGlobalSecretKey),
+ context.Config().Store.Type,
+ ),
+ )
+ c.isInitialised = true
+ return UserTokenAuthenticator(validator), nil
+}
+
+func (c *plugin) BeforeBootstrap(*plugins.MutablePluginContext, plugins.PluginConfig) error {
+ return nil
+}
+
+func (c *plugin) AfterBootstrap(context *plugins.MutablePluginContext, config plugins.PluginConfig) error {
+ if !c.isInitialised {
+ return nil
+ }
+ ctx := go_context.Background()
+ signingKeyManager := core_tokens.NewSigningKeyManager(context.ResourceManager(), issuer.UserTokenSigningKeyPrefix)
+ component := core_tokens.NewDefaultSigningKeyComponent(ctx, signingKeyManager, log, context.Extensions())
+ if err := context.ComponentManager().Add(component); err != nil {
+ return err
+ }
+ accessFn, ok := AccessStrategies[context.Config().Access.Type]
+ if !ok {
+ return errors.Errorf("no Access strategy for type %q", context.Config().Access.Type)
+ }
+ tokenIssuer := NewUserTokenIssuer(signingKeyManager)
+ if !context.Config().ApiServer.Authn.Tokens.EnableIssuer {
+ tokenIssuer = issuer.DisabledIssuer{}
+ }
+ if context.Config().ApiServer.Authn.Tokens.BootstrapAdminToken {
+ if err := context.ComponentManager().Add(NewAdminTokenBootstrap(tokenIssuer, context.ResourceManager(), context.Config())); err != nil {
+ return err
+ }
+ }
+ webService := server.NewWebService(tokenIssuer, accessFn(context))
+ context.APIManager().Add(webService)
+ return nil
+}
+
+func (c *plugin) Name() plugins.PluginName {
+ return PluginName
+}
+
+func (c *plugin) Order() int {
+ return plugins.EnvironmentPreparedOrder + 1
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
index 987f638..e2d5fd5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package tokens_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestTokens(t *testing.T) {
+ test.RunSpecs(t, "Tokens Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/authn/api-server/tokens/ws/client/client.go b/pkg/plugins/authn/api-server/tokens/ws/client/client.go
new file mode 100644
index 0000000..6f03af7
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/client/client.go
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package client
+
+import (
+ "time"
+
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws"
+ "github.com/apache/dubbo-kubernetes/pkg/tokens"
+ util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type UserTokenClient interface {
+ Generate(name string, groups []string, validFor time.Duration) (string, error)
+}
+
+var _ UserTokenClient = &httpUserTokenClient{}
+
+func NewHTTPUserTokenClient(client util_http.Client) UserTokenClient {
+ return &httpUserTokenClient{
+ client: tokens.NewTokenClient(client, "user"),
+ }
+}
+
+type httpUserTokenClient struct {
+ client tokens.TokenClient
+}
+
+func (h *httpUserTokenClient) Generate(name string, groups []string, validFor time.Duration) (string, error) {
+ tokenReq := &ws.UserTokenRequest{
+ Name: name,
+ Groups: groups,
+ ValidFor: validFor.String(),
+ }
+ return h.client.Generate(tokenReq)
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/request.go b/pkg/plugins/authn/api-server/tokens/ws/request.go
new file mode 100644
index 0000000..c33db61
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/request.go
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ws
+
+type UserTokenRequest struct {
+ Name string `json:"name"`
+ Groups []string `json:"groups"`
+ ValidFor string `json:"validFor"`
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go b/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go
new file mode 100644
index 0000000..053bd6e
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server
+
+import (
+ "net/http"
+ "time"
+
+ "github.com/emicklei/go-restful/v3"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/access"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws"
+)
+
+var log = core.Log.WithName("user-token-ws")
+
+type userTokenWebService struct {
+ issuer issuer.UserTokenIssuer
+ access access.GenerateUserTokenAccess
+}
+
+func NewWebService(issuer issuer.UserTokenIssuer, access access.GenerateUserTokenAccess) *restful.WebService {
+ webservice := userTokenWebService{
+ issuer: issuer,
+ access: access,
+ }
+ return webservice.createWs()
+}
+
+func (d *userTokenWebService) createWs() *restful.WebService {
+ webservice := new(restful.WebService).
+ Consumes(restful.MIME_JSON).
+ Produces(restful.MIME_JSON)
+ webservice.Path("/tokens/user").
+ Route(webservice.POST("").To(d.handleIdentityRequest))
+ return webservice
+}
+
+func (d *userTokenWebService) handleIdentityRequest(request *restful.Request, response *restful.Response) {
+ if err := d.access.ValidateGenerate(user.FromCtx(request.Request.Context())); err != nil {
+ errors.HandleError(request.Request.Context(), response, err, "Could not issue a token")
+ return
+ }
+
+ idReq := ws.UserTokenRequest{}
+ if err := request.ReadEntity(&idReq); err != nil {
+ log.Error(err, "Could not read a request")
+ response.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ verr := validators.ValidationError{}
+ if idReq.Name == "" {
+ verr.AddViolation("name", "cannot be empty")
+ }
+
+ var validFor time.Duration
+ if idReq.ValidFor == "" {
+ verr.AddViolation("validFor", "cannot be empty")
+ } else {
+ dur, err := time.ParseDuration(idReq.ValidFor)
+ if err != nil {
+ verr.AddViolation("validFor", "is invalid: "+err.Error())
+ }
+ validFor = dur
+ if validFor == 0 {
+ verr.AddViolation("validFor", "cannot be empty or nil")
+ }
+ }
+
+ if verr.HasViolations() {
+ errors.HandleError(request.Request.Context(), response, verr.OrNil(), "Invalid request")
+ return
+ }
+
+ token, err := d.issuer.Generate(request.Request.Context(), user.User{
+ Name: idReq.Name,
+ Groups: idReq.Groups,
+ }, validFor)
+ if err != nil {
+ errors.HandleError(request.Request.Context(), response, err, "Could not issue a token")
+ return
+ }
+
+ response.Header().Set("content-type", "text/plain")
+ if _, err := response.Write([]byte(token)); err != nil {
+ log.Error(err, "Could write a response")
+ }
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json
new file mode 100644
index 0000000..a6f4636
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json
@@ -0,0 +1,19 @@
+{
+ "type": "/std-errors",
+ "status": 400,
+ "title": "Invalid request",
+ "detail": "Resource is not valid",
+ "invalid_parameters": [
+ {
+ "field": "validFor",
+ "reason": "cannot be empty or nil"
+ }
+ ],
+ "details": "Resource is not valid",
+ "causes": [
+ {
+ "field": "validFor",
+ "message": "cannot be empty or nil"
+ }
+ ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json
new file mode 100644
index 0000000..5820ad0
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json
@@ -0,0 +1,19 @@
+{
+ "type": "/std-errors",
+ "status": 400,
+ "title": "Invalid request",
+ "detail": "Resource is not valid",
+ "invalid_parameters": [
+ {
+ "field": "validFor",
+ "reason": "cannot be empty"
+ }
+ ],
+ "details": "Resource is not valid",
+ "causes": [
+ {
+ "field": "validFor",
+ "message": "cannot be empty"
+ }
+ ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json
new file mode 100644
index 0000000..0896791
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json
@@ -0,0 +1,19 @@
+{
+ "type": "/std-errors",
+ "status": 400,
+ "title": "Invalid request",
+ "detail": "Resource is not valid",
+ "invalid_parameters": [
+ {
+ "field": "name",
+ "reason": "cannot be empty"
+ }
+ ],
+ "details": "Resource is not valid",
+ "causes": [
+ {
+ "field": "name",
+ "message": "cannot be empty"
+ }
+ ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json
new file mode 100644
index 0000000..376f8a6
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json
@@ -0,0 +1,7 @@
+{
+ "type": "/std-errors",
+ "status": 400,
+ "title": "Could not issue a token",
+ "detail": "issuing tokens using the control plane is disabled.",
+ "details": "issuing tokens using the control plane is disabled."
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
index 987f638..39e6b00 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package ws_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestWS(t *testing.T) {
+ test.RunSpecs(t, "User Tokens WS Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/authn/api-server/tokens/ws/ws_test.go b/pkg/plugins/authn/api-server/tokens/ws/ws_test.go
new file mode 100644
index 0000000..f9d0602
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/ws_test.go
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ws_test
+
+import (
+ "context"
+ "encoding/json"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "path"
+ "strings"
+ "time"
+
+ "github.com/emicklei/go-restful/v3"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/client"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/server"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+ "github.com/apache/dubbo-kubernetes/pkg/test/matchers"
+ util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type noopGenerateUserTokenAccess struct{}
+
+func (n *noopGenerateUserTokenAccess) ValidateGenerate(user.User) error {
+ return nil
+}
+
+var _ = Describe("Auth Tokens WS", func() {
+ var userTokenClient client.UserTokenClient
+ var userTokenValidator issuer.UserTokenValidator
+ var httpClient util_http.Client
+
+ BeforeEach(func() {
+ resManager := manager.NewResourceManager(memory.NewStore())
+ signingKeyManager := core_tokens.NewSigningKeyManager(resManager, issuer.UserTokenSigningKeyPrefix)
+ tokenIssuer := issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+ userTokenValidator = issuer.NewUserTokenValidator(
+ core_tokens.NewValidator(
+ core.Log.WithName("test"),
+ []core_tokens.SigningKeyAccessor{
+ core_tokens.NewSigningKeyAccessor(resManager, issuer.UserTokenSigningKeyPrefix),
+ },
+ core_tokens.NewRevocations(resManager, issuer.UserTokenRevocationsGlobalSecretKey),
+ store_config.MemoryStore,
+ ),
+ )
+
+ Expect(signingKeyManager.CreateDefaultSigningKey(context.Background())).To(Succeed())
+ ws := server.NewWebService(tokenIssuer, &noopGenerateUserTokenAccess{})
+
+ container := restful.NewContainer()
+ container.Add(ws)
+ srv := httptest.NewServer(container)
+
+ baseURL, err := url.Parse(srv.URL)
+ Expect(err).ToNot(HaveOccurred())
+ httpClient = util_http.ClientWithBaseURL(http.DefaultClient, baseURL, nil)
+ userTokenClient = client.NewHTTPUserTokenClient(httpClient)
+
+ // wait for the server
+ Eventually(func() error {
+ _, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, time.Hour)
+ return err
+ }).ShouldNot(HaveOccurred())
+ })
+
+ It("should generate token", func() {
+ // when
+ token, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, 1*time.Hour)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ u, err := userTokenValidator.Validate(context.Background(), token)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(u.Name).To(Equal("john.doe@example.com"))
+ Expect(u.Groups).To(Equal([]string{"team-a"}))
+ })
+
+ It("should throw an error when name is not passed", func() {
+ // when
+ _, err := userTokenClient.Generate("", nil, 1*time.Hour)
+
+ // then
+ bytes, err := json.MarshalIndent(err, "", " ")
+ Expect(err).ToNot(HaveOccurred())
+ Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-no-name.golden.json")))
+ })
+
+ It("should throw an error with 0 for validFor", func() {
+ // when
+ _, err := userTokenClient.Generate("foo@example.com", nil, 0)
+
+ // then
+ bytes, err := json.MarshalIndent(err, "", " ")
+ Expect(err).ToNot(HaveOccurred())
+ Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-0-validFor.golden.json")))
+ })
+
+ It("should throw an error if validFor is not present", func() {
+ // given invalid request (cannot be implemented using UserTokenClient)
+ req, err := http.NewRequest("POST", "/tokens/user", strings.NewReader(`{"name": "xyz"}`))
+ req.Header.Add("content-type", "application/json")
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ resp, err := httpClient.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+ defer resp.Body.Close()
+
+ // then
+ bytes, err := io.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-missing-validFor.golden.json")))
+ })
+
+ It("should throw an error when issuer is disabled", func() {
+ container := restful.NewContainer()
+ ws := server.NewWebService(issuer.DisabledIssuer{}, &noopGenerateUserTokenAccess{})
+ container.Add(ws)
+ srv := httptest.NewServer(container)
+
+ baseURL, err := url.Parse(srv.URL)
+ Expect(err).ToNot(HaveOccurred())
+ httpClient := util_http.ClientWithBaseURL(http.DefaultClient, baseURL, nil)
+ userTokenClient := client.NewHTTPUserTokenClient(httpClient)
+
+ Eventually(func(g Gomega) {
+ _, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, 1*time.Hour)
+ bytes, err := json.MarshalIndent(err, "", " ")
+ Expect(err).ToNot(HaveOccurred())
+ Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-token-issuer-disabled.golden.json")))
+ }, "10s", "100ms").Should(Succeed())
+ })
+})
diff --git a/pkg/plugins/authn/api/authn.go b/pkg/plugins/authn/api/authn.go
new file mode 100644
index 0000000..09972f5
--- /dev/null
+++ b/pkg/plugins/authn/api/authn.go
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+
+type AuthnPlugin interface {
+ Validate(map[string]string) error
+ DecorateClient(util_http.Client, map[string]string) (util_http.Client, error)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/builtin/builtin_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/builtin/builtin_suite_test.go
index 987f638..589988b 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/builtin/builtin_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package builtin_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestCaBuiltin(t *testing.T) {
+ test.RunSpecs(t, "CA Builtin Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/ca/builtin/ca.go b/pkg/plugins/ca/builtin/ca.go
new file mode 100644
index 0000000..fc25ea3
--- /dev/null
+++ b/pkg/plugins/ca/builtin/ca.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package builtin
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "math/big"
+ "net/url"
+ "time"
+
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+ util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+ "github.com/pkg/errors"
+
+ "github.com/spiffe/go-spiffe/v2/spiffeid"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+)
+
+const (
+ DefaultAllowedClockSkew = 10 * time.Second
+ DefaultCACertValidityPeriod = 10 * 365 * 24 * time.Hour
+)
+
+type certOptsFn = func(*x509.Certificate)
+
+func withExpirationTime(expiration time.Duration) certOptsFn {
+ return func(certificate *x509.Certificate) {
+ now := core.Now()
+ certificate.NotAfter = now.Add(expiration)
+ }
+}
+
+func newRootCa(mesh string, rsaBits int, certOpts ...certOptsFn) (*core_ca.KeyPair, error) {
+ if rsaBits == 0 {
+ rsaBits = util_rsa.DefaultKeySize
+ }
+ key, err := util_rsa.GenerateKey(rsaBits)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate a private key")
+ }
+ cert, err := newCACert(key, mesh, certOpts...)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate X509 certificate")
+ }
+ return util_tls.ToKeyPair(key, cert)
+}
+
+func newCACert(signer crypto.Signer, trustDomain string, certOpts ...certOptsFn) ([]byte, error) {
+ subject := pkix.Name{
+ Organization: []string{"Dubbo"},
+ OrganizationalUnit: []string{"Mesh"},
+ CommonName: trustDomain,
+ }
+ now := core.Now()
+ notBefore := now.Add(-DefaultAllowedClockSkew)
+ notAfter := now.Add(DefaultCACertValidityPeriod)
+
+ template, err := caTemplate(trustDomain, subject, signer.Public(), notBefore, notAfter, big.NewInt(0))
+ if err != nil {
+ return nil, err
+ }
+
+ for _, opt := range certOpts {
+ opt(template)
+ }
+ return x509.CreateCertificate(rand.Reader, template, template, signer.Public(), signer)
+}
+
+func caTemplate(trustDomain string, subject pkix.Name, publicKey crypto.PublicKey, notBefore, notAfter time.Time, serialNumber *big.Int) (*x509.Certificate, error) {
+ domain, err := spiffeid.TrustDomainFromString(trustDomain)
+ if err != nil {
+ return nil, err
+ }
+ uri, err := spiffeid.FromSegments(domain)
+ if err != nil {
+ return nil, err
+ }
+ return &x509.Certificate{
+ SerialNumber: serialNumber,
+ Subject: subject,
+ URIs: []*url.URL{uri.URL()},
+ NotBefore: notBefore,
+ NotAfter: notAfter,
+ KeyUsage: x509.KeyUsageCertSign |
+ x509.KeyUsageCRLSign,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKey: publicKey,
+ }, nil
+}
diff --git a/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go b/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go
new file mode 100644
index 0000000..95bac80
--- /dev/null
+++ b/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go
@@ -0,0 +1,237 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: builtin_ca_config.proto
+
+package config
+
+import (
+ wrappers "github.com/golang/protobuf/ptypes/wrappers"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// BuiltinCertificateAuthorityConfig defines configuration for Builtin CA
+// plugin
+type BuiltinCertificateAuthorityConfig struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Configuration of CA Certificate
+ CaCert *BuiltinCertificateAuthorityConfig_CaCert `protobuf:"bytes,1,opt,name=caCert,proto3" json:"caCert,omitempty"`
+}
+
+func (x *BuiltinCertificateAuthorityConfig) Reset() {
+ *x = BuiltinCertificateAuthorityConfig{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_builtin_ca_config_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BuiltinCertificateAuthorityConfig) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuiltinCertificateAuthorityConfig) ProtoMessage() {}
+
+func (x *BuiltinCertificateAuthorityConfig) ProtoReflect() protoreflect.Message {
+ mi := &file_builtin_ca_config_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuiltinCertificateAuthorityConfig.ProtoReflect.Descriptor instead.
+func (*BuiltinCertificateAuthorityConfig) Descriptor() ([]byte, []int) {
+ return file_builtin_ca_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BuiltinCertificateAuthorityConfig) GetCaCert() *BuiltinCertificateAuthorityConfig_CaCert {
+ if x != nil {
+ return x.CaCert
+ }
+ return nil
+}
+
+// CaCert defines configuration for Certificate of CA.
+type BuiltinCertificateAuthorityConfig_CaCert struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // RSAbits of the certificate
+ RSAbits *wrappers.UInt32Value `protobuf:"bytes,1,opt,name=RSAbits,proto3" json:"RSAbits,omitempty"`
+ // Expiration time of the certificate
+ Expiration string `protobuf:"bytes,2,opt,name=expiration,proto3" json:"expiration,omitempty"`
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) Reset() {
+ *x = BuiltinCertificateAuthorityConfig_CaCert{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_builtin_ca_config_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuiltinCertificateAuthorityConfig_CaCert) ProtoMessage() {}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) ProtoReflect() protoreflect.Message {
+ mi := &file_builtin_ca_config_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuiltinCertificateAuthorityConfig_CaCert.ProtoReflect.Descriptor instead.
+func (*BuiltinCertificateAuthorityConfig_CaCert) Descriptor() ([]byte, []int) {
+ return file_builtin_ca_config_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) GetRSAbits() *wrappers.UInt32Value {
+ if x != nil {
+ return x.RSAbits
+ }
+ return nil
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) GetExpiration() string {
+ if x != nil {
+ return x.Expiration
+ }
+ return ""
+}
+
+var File_builtin_ca_config_proto protoreflect.FileDescriptor
+
+var file_builtin_ca_config_proto_rawDesc = []byte{
+ 0x0a, 0x17, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x64, 0x75, 0x62, 0x62, 0x6f,
+ 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x63, 0x61, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61,
+ 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd9, 0x01, 0x0a, 0x21,
+ 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x12, 0x52, 0x0a, 0x06, 0x63, 0x61, 0x43, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x3a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
+ 0x73, 0x2e, 0x63, 0x61, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x52, 0x06, 0x63,
+ 0x61, 0x43, 0x65, 0x72, 0x74, 0x1a, 0x60, 0x0a, 0x06, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x12,
+ 0x36, 0x0a, 0x07, 0x52, 0x53, 0x41, 0x62, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07,
+ 0x52, 0x53, 0x41, 0x62, 0x69, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70,
+ 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75,
+ 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62,
+ 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x70, 0x6b,
+ 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61, 0x2f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_builtin_ca_config_proto_rawDescOnce sync.Once
+ file_builtin_ca_config_proto_rawDescData = file_builtin_ca_config_proto_rawDesc
+)
+
+func file_builtin_ca_config_proto_rawDescGZIP() []byte {
+ file_builtin_ca_config_proto_rawDescOnce.Do(func() {
+ file_builtin_ca_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_builtin_ca_config_proto_rawDescData)
+ })
+ return file_builtin_ca_config_proto_rawDescData
+}
+
+var file_builtin_ca_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_builtin_ca_config_proto_goTypes = []interface{}{
+ (*BuiltinCertificateAuthorityConfig)(nil), // 0: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig
+ (*BuiltinCertificateAuthorityConfig_CaCert)(nil), // 1: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert
+ (*wrappers.UInt32Value)(nil), // 2: google.protobuf.UInt32Value
+}
+var file_builtin_ca_config_proto_depIdxs = []int32{
+ 1, // 0: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.caCert:type_name -> dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert
+ 2, // 1: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert.RSAbits:type_name -> google.protobuf.UInt32Value
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_builtin_ca_config_proto_init() }
+func file_builtin_ca_config_proto_init() {
+ if File_builtin_ca_config_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_builtin_ca_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BuiltinCertificateAuthorityConfig); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_builtin_ca_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BuiltinCertificateAuthorityConfig_CaCert); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_builtin_ca_config_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_builtin_ca_config_proto_goTypes,
+ DependencyIndexes: file_builtin_ca_config_proto_depIdxs,
+ MessageInfos: file_builtin_ca_config_proto_msgTypes,
+ }.Build()
+ File_builtin_ca_config_proto = out.File
+ file_builtin_ca_config_proto_rawDesc = nil
+ file_builtin_ca_config_proto_goTypes = nil
+ file_builtin_ca_config_proto_depIdxs = nil
+}
diff --git a/pkg/plugins/ca/builtin/config/builtin_ca_config.proto b/pkg/plugins/ca/builtin/config/builtin_ca_config.proto
new file mode 100644
index 0000000..fef3715
--- /dev/null
+++ b/pkg/plugins/ca/builtin/config/builtin_ca_config.proto
@@ -0,0 +1,22 @@
+syntax = "proto3";
+
+package dubbo.plugins.ca;
+
+option go_package = "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/config";
+
+import "google/protobuf/wrappers.proto";
+
+// BuiltinCertificateAuthorityConfig defines configuration for Builtin CA
+// plugin
+message BuiltinCertificateAuthorityConfig {
+ // CaCert defines configuration for Certificate of CA.
+ message CaCert {
+ // RSAbits of the certificate
+ google.protobuf.UInt32Value RSAbits = 1;
+ // Expiration time of the certificate
+ string expiration = 2;
+ }
+
+ // Configuration of CA Certificate
+ CaCert caCert = 1;
+}
diff --git a/pkg/plugins/ca/builtin/manager.go b/pkg/plugins/ca/builtin/manager.go
new file mode 100644
index 0000000..c75ca90
--- /dev/null
+++ b/pkg/plugins/ca/builtin/manager.go
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package builtin
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/asaskevich/govalidator"
+ "github.com/pkg/errors"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ ca_issuer "github.com/apache/dubbo-kubernetes/pkg/core/ca/issuer"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_system "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ core_validators "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin/config"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var log = core.Log.WithName("ca").WithName("builtin")
+
+const MaxBackendNameLength = 255
+
+type builtinCaManager struct {
+ secretManager manager.ResourceManager
+}
+
+func NewBuiltinCaManager(secretManager manager.ResourceManager) core_ca.Manager {
+ return &builtinCaManager{
+ secretManager: secretManager,
+ }
+}
+
+var _ core_ca.Manager = &builtinCaManager{}
+
+// EnsureBackends ensures the CA secrets for the given mesh and backends.
+func (b *builtinCaManager) EnsureBackends(ctx context.Context, mesh core_model.Resource, backends []*mesh_proto.CertificateAuthorityBackend) error {
+ for _, backend := range backends {
+ meshName := mesh.GetMeta().GetName()
+ _, err := b.getCa(ctx, meshName, backend.Name)
+ if err == nil {
+ log.V(1).Info("CA secrets already exist. Nothing to create", "mesh", mesh, "backend", backend.Name)
+ continue
+ }
+
+ if !core_store.IsResourceNotFound(err) {
+ return err
+ }
+
+ if err := b.create(ctx, mesh, backend); err != nil {
+ return errors.Wrapf(err, "failed to create CA for mesh %q and backend %q", mesh, backend.Name)
+ }
+ log.Info("CA created", "mesh", meshName)
+ }
+ return nil
+}
+
+// validate config,backend name
+func (b *builtinCaManager) ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error {
+ verr := core_validators.ValidationError{}
+ cfg := &config.BuiltinCertificateAuthorityConfig{}
+ if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+ verr.AddViolation("", "could not convert backend config: "+err.Error())
+ }
+ backendNameWithPrefix := createSecretName(mesh, backend.Name, "cert")
+ if len(backendNameWithPrefix) > MaxBackendNameLength {
+ verr.AddViolationAt(core_validators.RootedAt("mtls").Field("backends").Field("name"), fmt.Sprintf("Backend name is too long. Max length: %d", MaxBackendNameLength))
+ }
+ if !govalidator.IsDNSName(backendNameWithPrefix) || !govalidator.IsLowerCase(backendNameWithPrefix) {
+ verr.AddViolationAt(core_validators.RootedAt("mtls").Field("backends").Field("name"), fmt.Sprintf("%q name must be valid dns name", backend.Name))
+ }
+ return verr.OrNil()
+}
+
+func (b *builtinCaManager) UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error) {
+ return []string{
+ certSecretResKey(mesh, backend.Name).Name,
+ keySecretResKey(mesh, backend.Name).Name,
+ }, nil
+}
+
+func (b *builtinCaManager) create(ctx context.Context, mesh core_model.Resource, backend *mesh_proto.CertificateAuthorityBackend) error {
+ meshName := mesh.GetMeta().GetName()
+ cfg := &config.BuiltinCertificateAuthorityConfig{}
+ if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+ return errors.Wrap(err, "could not convert backend config to BuiltinCertificateAuthorityConfig")
+ }
+
+ var opts []certOptsFn
+ if cfg.GetCaCert().GetExpiration() != "" {
+ duration, err := core_mesh.ParseDuration(cfg.GetCaCert().GetExpiration())
+ if err != nil {
+ return err
+ }
+ opts = append(opts, withExpirationTime(duration))
+ }
+ keyPair, err := newRootCa(meshName, int(cfg.GetCaCert().GetRSAbits().GetValue()), opts...)
+ if err != nil {
+ return errors.Wrapf(err, "failed to generate a Root CA cert for Mesh %q", meshName)
+ }
+
+ certSecret := &core_system.SecretResource{
+ Spec: &system_proto.Secret{
+ Data: util_proto.Bytes(keyPair.CertPEM),
+ },
+ }
+ if err := b.secretManager.Create(ctx, certSecret, core_store.CreateWithOwner(mesh), core_store.CreateBy(certSecretResKey(meshName, backend.Name))); err != nil {
+ if !errors.Is(err, &core_store.ResourceConflictError{}) {
+ return err
+ }
+ log.V(1).Info("CA certificate already exists. Nothing to create", "mesh", meshName, "backend", backend.Name)
+ }
+
+ keySecret := &core_system.SecretResource{
+ Spec: &system_proto.Secret{
+ Data: util_proto.Bytes(keyPair.KeyPEM),
+ },
+ }
+ if err := b.secretManager.Create(ctx, keySecret, core_store.CreateWithOwner(mesh), core_store.CreateBy(keySecretResKey(meshName, backend.Name))); err != nil {
+ if !errors.Is(err, &core_store.ResourceConflictError{}) {
+ return err
+ }
+ log.V(1).Info("CA secret key already exists. Nothing to create", "mesh", meshName, "backend", backend.Name)
+ }
+ return nil
+}
+
+func certSecretResKey(mesh string, backendName string) core_model.ResourceKey {
+ return core_model.ResourceKey{
+ Mesh: mesh,
+ Name: createSecretName(mesh, backendName, "cert"),
+ }
+}
+
+func keySecretResKey(mesh string, backendName string) core_model.ResourceKey {
+ return core_model.ResourceKey{
+ Mesh: mesh,
+ Name: createSecretName(mesh, backendName, "key"),
+ }
+}
+
+func createSecretName(mesh string, backendName string, secretType string) string {
+ return fmt.Sprintf("%s.ca-builtin-%s-%s", mesh, secretType, backendName) // we add mesh as a prefix to have uniqueness of Secret names on K8S
+}
+
+func (b *builtinCaManager) GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]core_ca.Cert, error) {
+ ca, err := b.getCa(ctx, mesh, backend.Name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+ }
+ return []core_ca.Cert{ca.CertPEM}, nil
+}
+
+func (b *builtinCaManager) GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (core_ca.KeyPair, error) {
+ ca, err := b.getCa(ctx, mesh, backend.Name)
+ if err != nil {
+ return core_ca.KeyPair{}, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+ }
+
+ var opts []ca_issuer.CertOptsFn
+ if backend.GetDpCert().GetRotation().GetExpiration() != "" {
+ duration, err := core_mesh.ParseDuration(backend.GetDpCert().GetRotation().Expiration)
+ if err != nil {
+ return core_ca.KeyPair{}, err
+ }
+ opts = append(opts, ca_issuer.WithExpirationTime(duration))
+ }
+ keyPair, err := ca_issuer.NewWorkloadCert(ca, mesh, tags, opts...)
+ if err != nil {
+ return core_ca.KeyPair{}, errors.Wrapf(err, "failed to generate a Workload Identity cert for tags %q in Mesh %q using backend %q", tags.String(), mesh, backend)
+ }
+ return *keyPair, nil
+}
+
+func (b *builtinCaManager) getCa(ctx context.Context, mesh string, backendName string) (core_ca.KeyPair, error) {
+ certSecret := core_system.NewSecretResource()
+ if err := b.secretManager.Get(ctx, certSecret, core_store.GetBy(certSecretResKey(mesh, backendName))); err != nil {
+ return core_ca.KeyPair{}, err
+ }
+
+ keySecret := core_system.NewSecretResource()
+ if err := b.secretManager.Get(ctx, keySecret, core_store.GetBy(keySecretResKey(mesh, backendName))); err != nil {
+ return core_ca.KeyPair{}, err
+ }
+
+ return core_ca.KeyPair{
+ CertPEM: certSecret.Spec.Data.Value,
+ KeyPEM: keySecret.Spec.Data.Value,
+ }, nil
+}
diff --git a/pkg/plugins/ca/builtin/manager_test.go b/pkg/plugins/ca/builtin/manager_test.go
new file mode 100644
index 0000000..ae100e6
--- /dev/null
+++ b/pkg/plugins/ca/builtin/manager_test.go
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package builtin_test
+
+import (
+ "context"
+ "crypto/x509"
+ "encoding/pem"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "sigs.k8s.io/yaml"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+ secret_manager "github.com/apache/dubbo-kubernetes/pkg/core/secrets/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin/config"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Builtin CA Manager", func() {
+ var secretManager manager.ResourceManager
+ var caManager core_ca.Manager
+ var mesh *core_mesh.MeshResource
+
+ now := time.Now()
+
+ BeforeEach(func() {
+ core.Now = func() time.Time {
+ return now
+ }
+ memoryStore := memory.NewStore()
+ rm := manager.NewResourceManager(memoryStore)
+ secretManager = secret_manager.NewSecretManager(store.NewSecretStore(memoryStore), cipher.None(), nil, false)
+ caManager = builtin.NewBuiltinCaManager(secretManager)
+ mesh = core_mesh.NewMeshResource()
+ // Since mesh is the owner of secrets we can't operate on secrets without having the mesh in the store
+ err := rm.Create(context.Background(), mesh, core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ AfterEach(func() {
+ core.Now = time.Now
+ })
+
+ Context("EnsureBackends", func() {
+ It("should create a CA", func() {
+ // given
+ backends := []*mesh_proto.CertificateAuthorityBackend{{
+ Name: "builtin-1",
+ Type: "builtin",
+ }}
+
+ // when
+ err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // and key+cert are stored as a secrets
+ secretRes := system.NewSecretResource()
+ err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+ Expect(err).ToNot(HaveOccurred())
+ Expect(secretRes.Spec.GetData().GetValue()).ToNot(BeEmpty())
+
+ keyRes := system.NewSecretResource()
+ err = secretManager.Get(context.Background(), keyRes, core_store.GetByKey("default.ca-builtin-key-builtin-1", "default"))
+ Expect(err).ToNot(HaveOccurred())
+ Expect(keyRes.Spec.GetData().GetValue()).ToNot(BeEmpty())
+
+ // when called Ensured after CA is already created
+ err = caManager.EnsureBackends(context.Background(), mesh, backends)
+
+ // then no error happens
+ Expect(err).ToNot(HaveOccurred())
+
+ // and CA has default parameters
+ block, _ := pem.Decode(secretRes.Spec.Data.Value)
+ cert, err := x509.ParseCertificate(block.Bytes)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(cert.NotAfter).To(Equal(core.Now().UTC().Add(10 * 365 * 24 * time.Hour).Truncate(time.Second)))
+ })
+
+ It("should create a configured CA", func() {
+ // given
+ backends := []*mesh_proto.CertificateAuthorityBackend{{
+ Name: "builtin-1",
+ Type: "builtin",
+ Conf: util_proto.MustToStruct(&config.BuiltinCertificateAuthorityConfig{
+ CaCert: &config.BuiltinCertificateAuthorityConfig_CaCert{
+ RSAbits: util_proto.UInt32(uint32(2048)),
+ Expiration: "1m",
+ },
+ }),
+ }}
+
+ // when
+ err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // and CA has configured parameters
+ secretRes := system.NewSecretResource()
+ err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+ Expect(err).ToNot(HaveOccurred())
+ block, _ := pem.Decode(secretRes.Spec.Data.Value)
+ cert, err := x509.ParseCertificate(block.Bytes)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(cert.NotAfter).To(Equal(core.Now().UTC().Add(time.Minute).Truncate(time.Second)))
+ })
+
+ It("should ensure first backend and then second", func() {
+ // given
+ backends := []*mesh_proto.CertificateAuthorityBackend{{
+ Name: "builtin-1",
+ Type: "builtin",
+ }}
+
+ // when
+ err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // when second one is added AFTER the CA for the first one was created
+ backends = append(backends, &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-2",
+ Type: "builtin",
+ })
+ err = caManager.EnsureBackends(context.Background(), mesh, backends)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // and both CAs have their keys
+ secretRes := system.NewSecretResource()
+ err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+ Expect(err).ToNot(HaveOccurred())
+
+ secretRes = system.NewSecretResource()
+ err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-2", "default"))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
+ Context("ValidateBackend", func() {
+ It("should validate backend", func() {
+ // given
+ mesh := "default"
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-1",
+ Type: "builtin",
+ }
+
+ // when
+ err := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("should fail validation when name too long", func() {
+ // given
+ mesh := "default"
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long",
+ Type: "builtin",
+ }
+
+ // when
+ verr := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+ // then
+ actual, err := yaml.Marshal(verr)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(actual).To(MatchYAML(`
+ violations:
+ - field: mtls.backends.name
+ message: 'Backend name is too long. Max length: 255'`))
+ })
+
+ It("should fail validation when backend name contains uppercase letters", func() {
+ // given
+ mesh := "default"
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtinCA",
+ Type: "builtin",
+ }
+
+ // when
+ verr := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+ // then
+ actual, err := yaml.Marshal(verr)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(actual).To(MatchYAML(`
+ violations:
+ - field: mtls.backends.name
+ message: '"builtinCA" name must be valid dns name'`))
+ })
+ })
+
+ Context("GetRootCert", func() {
+ It("should retrieve created certs", func() {
+ // given
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-1",
+ Type: "builtin",
+ }
+ err := caManager.EnsureBackends(context.Background(), mesh, []*mesh_proto.CertificateAuthorityBackend{backend})
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ certs, err := caManager.GetRootCert(context.Background(), mesh.GetMeta().GetName(), backend)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(certs).To(HaveLen(1))
+ Expect(certs[0]).ToNot(BeEmpty())
+ })
+
+ It("should throw an error on retrieving certs on CA that was not created", func() {
+ // given
+ mesh := "default"
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-non-existent",
+ Type: "builtin",
+ }
+
+ // when
+ _, err := caManager.GetRootCert(context.Background(), mesh, backend)
+
+ // then
+ Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "builtin-non-existent": Resource not found: type="Secret" name="default.ca-builtin-cert-builtin-non-existent" mesh="default"`))
+ })
+ })
+
+ Context("GenerateDataplaneCert", func() {
+ It("should generate dataplane certs", func() {
+ // given
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-1",
+ Type: "builtin",
+ DpCert: &mesh_proto.CertificateAuthorityBackend_DpCert{
+ Rotation: &mesh_proto.CertificateAuthorityBackend_DpCert_Rotation{
+ Expiration: "1s",
+ },
+ },
+ }
+ err := caManager.EnsureBackends(context.Background(), mesh, []*mesh_proto.CertificateAuthorityBackend{backend})
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ tags := map[string]map[string]bool{
+ "dubbo.io/service": {
+ "web": true,
+ "web-api": true,
+ },
+ "version": {
+ "v1": true,
+ },
+ }
+ pair, err := caManager.GenerateDataplaneCert(context.Background(), mesh.GetMeta().GetName(), backend, tags)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(pair.KeyPEM).ToNot(BeEmpty())
+ Expect(pair.CertPEM).ToNot(BeEmpty())
+
+ // and should generate cert for dataplane with spiffe URI
+ block, _ := pem.Decode(pair.CertPEM)
+ cert, err := x509.ParseCertificate(block.Bytes)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(cert.URIs).To(HaveLen(5))
+ Expect(cert.URIs[0].String()).To(Equal("spiffe://default/web"))
+ Expect(cert.URIs[1].String()).To(Equal("spiffe://default/web-api"))
+ Expect(cert.URIs[2].String()).To(Equal("dubbo://dubbo.io/service/web"))
+ Expect(cert.URIs[3].String()).To(Equal("dubbo://dubbo.io/service/web-api"))
+ Expect(cert.URIs[4].String()).To(Equal("dubbo://version/v1"))
+ Expect(cert.NotAfter).To(Equal(now.UTC().Truncate(time.Second).Add(1 * time.Second))) // time in cert is in UTC and truncated to seconds
+ })
+
+ It("should throw an error on generate dataplane certs on non-existing CA", func() {
+ // given
+ mesh := "default"
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "builtin-non-existent",
+ Type: "builtin",
+ }
+
+ // when
+ _, err := caManager.GenerateDataplaneCert(context.Background(), mesh, backend, mesh_proto.MultiValueTagSet{})
+
+ // then
+ Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "builtin-non-existent": Resource not found: type="Secret" name="default.ca-builtin-cert-builtin-non-existent" mesh="default"`))
+ })
+ })
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/builtin/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/builtin/plugin.go
index 987f638..f2d8787 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/builtin/plugin.go
@@ -15,25 +15,21 @@
* limitations under the License.
*/
-package dubbo
+package builtin
import (
- "archive/zip"
- "bytes"
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.CaPlugin = &plugin{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+ core_plugins.Register(core_plugins.CaBuiltin, &plugin{})
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p plugin) NewCaManager(context core_plugins.PluginContext, config core_plugins.PluginConfig) (ca.Manager, error) {
+ return NewBuiltinCaManager(context.ResourceManager()), nil
+}
diff --git a/pkg/plugins/ca/provided/ca_cert_validator.go b/pkg/plugins/ca/provided/ca_cert_validator.go
new file mode 100644
index 0000000..ff767e0
--- /dev/null
+++ b/pkg/plugins/ca/provided/ca_cert_validator.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provided
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+func ValidateCaCert(signingPair util_tls.KeyPair) error {
+ err := validateCaCert(signingPair)
+ return err.OrNil()
+}
+
+func validateCaCert(signingPair util_tls.KeyPair) validators.ValidationError {
+ var verr validators.ValidationError
+ tlsKeyPair, err := tls.X509KeyPair(signingPair.CertPEM, signingPair.KeyPEM)
+ if err != nil {
+ verr.AddViolation("cert", fmt.Sprintf("not a valid TLS key pair: %s", err))
+ return verr
+ }
+ for i, certificate := range tlsKeyPair.Certificate {
+ path := validators.RootedAt("cert").Index(i)
+ cert, err := x509.ParseCertificate(certificate)
+ if err != nil {
+ verr.AddViolationAt(path, fmt.Sprintf("not a valid x509 certificate: %s", err))
+ return verr
+ }
+ if !cert.IsCA {
+ verr.AddViolationAt(path, "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)")
+ }
+ if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
+ verr.AddViolationAt(path, "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)")
+ }
+ if cert.KeyUsage&x509.KeyUsageKeyAgreement != 0 {
+ verr.AddViolationAt(path, "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)")
+ }
+ }
+ return verr
+}
diff --git a/pkg/plugins/ca/provided/ca_cert_validator_test.go b/pkg/plugins/ca/provided/ca_cert_validator_test.go
new file mode 100644
index 0000000..820d680
--- /dev/null
+++ b/pkg/plugins/ca/provided/ca_cert_validator_test.go
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provided_test
+
+import (
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "math/big"
+ "os"
+ "path/filepath"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "github.com/pkg/errors"
+ "sigs.k8s.io/yaml"
+
+ . "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+var _ = Describe("ValidateCaCert()", func() {
+ It("should accept proper CA certificates", func() {
+ // when
+ cert, err := os.ReadFile(filepath.Join("testdata", "ca.pem"))
+ Expect(err).ToNot(HaveOccurred())
+ key, err := os.ReadFile(filepath.Join("testdata", "ca.key"))
+ Expect(err).ToNot(HaveOccurred())
+
+ signingPair := &util_tls.KeyPair{
+ CertPEM: cert,
+ KeyPEM: key,
+ }
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ err = ValidateCaCert(*signingPair)
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ NewSelfSignedCert := func(newTemplate func() *x509.Certificate) (*util_tls.KeyPair, error) {
+ key, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate a private key")
+ }
+ template := newTemplate()
+ template.PublicKey = key.Public()
+ cert, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to sign X509 certificate")
+ }
+ return util_tls.ToKeyPair(key, cert)
+ }
+
+ type testCase struct {
+ input util_tls.KeyPair
+ expectedErr string
+ }
+
+ DescribeTable("should reject invalid input",
+ func(givenFunc func() testCase) {
+ given := givenFunc()
+
+ // when
+ err := ValidateCaCert(given.input)
+ // then
+ Expect(err).To(HaveOccurred())
+
+ // when
+ actual, err := yaml.Marshal(err)
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ // and
+ Expect(actual).To(MatchYAML(given.expectedErr))
+ },
+ Entry("empty key pair", func() testCase {
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert
+ message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'
+`,
+ }
+ }),
+ Entry("bad plain text values", func() testCase {
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert
+ message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'
+`,
+ input: util_tls.KeyPair{
+ CertPEM: []byte("CERT"),
+ KeyPEM: []byte("KEY"),
+ },
+ }
+ }),
+ Entry("cert and key don't match", func() testCase {
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert
+ message: 'not a valid TLS key pair: tls: private key does not match public key'
+`,
+ input: util_tls.KeyPair{
+ CertPEM: []byte(`
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAwMQ0wCwYDVQQKEwRLdW1h
+MQ0wCwYDVQQLEwRNZXNoMRAwDgYDVQQDEwdkZWZhdWx0MB4XDTIwMDEyOTE2MDgw
+NFoXDTMwMDEyNjE2MDgxNFowMDENMAsGA1UEChMES3VtYTENMAsGA1UECxMETWVz
+aDEQMA4GA1UEAxMHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANhewNnHZI0f+55vsm+iGej9NAYFtCb2FNzFHZlGcu0F07YSAyorPJuM+6V3
+BFcY2IkWHL8WOooNmJ0X/yzBd4RSrb3TacGKtaDayRjTo8JOW7Nlh+WvwR18KHjC
+QXDjlqkmfExdYIUZjOqJOhu9nO59fqz0SJFvo2WKkkP7CaTLQXt1p3+Hm1Xo5WCX
+ZfD7W57YhNBZZLljip/N8pDL7b2Vkhe+txbv/PqVrDRMGoyRBnPNAfS7SPocRbcE
+S9th2CesNu+Iwltu4gJBOQbpydBIjJvr1zrx/zxbxM+EbqbGr6gwquGvKTyXHq20
+u5CE4tWy3GKSh5LEVItPS066d5UCAwEAAaNAMD4wDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wGwYDVR0RBBQwEoYQc3BpZmZlOi8vZGVmYXVsdDANBgkq
+hkiG9w0BAQsFAAOCAQEAMvMqCzbjEveuMlTch9q+/6KcFVUkwQcTcxxs0MPnw5Lw
+hY6xo7FvIHNLJDRlShoAyjI6OJZobJ7PFaFnIXWlNcN1F+gA9OZSSWYgJNJl4zee
+eS2pHgbxZ6OJqCbDGYWekF3d1vEcI3gaRfxFDVa8eJFBq+B0v2Pho8A2bY5srO0S
+LG//nB619RAlzta6BxweiCmFxPyB+oqJl3X9NfWC7988CzfZAqHA+CqO6tJS5i53
+WMciH6+W7o9wdsBrfFVx2tGZqc4bZgoZptHFieqz7YBnT0Ozg+NwBU6apAtAc5Ym
+DMoTRP2Vo+BEm4uS4GcIFZYqrOsPuuyMuBd0NDE33g==
+-----END CERTIFICATE-----
+`),
+ KeyPEM: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvh4DhQDuE/oXjEJzt1TmgmM6s5DtsIYIEPssxdDiDEjmJzhG
+APd0BmAfrz3/fXZ6CqqqfAvTKkTDGjSN0hGtt1thF72weNfmQX9c1gUukEA0gERS
+OsSWlzp8Azc5fjXtl5jiQkvaapNoQ8PljLc8SEsgndvdxD1tDVWN6h90bMgtuqyK
+M7Fn/1+nwhRExKRTOSlR0iS9eddWPcN3HNGrl3vjCF0oUF/79+nJJqeaXawt0+2P
+CMkxY6T6yzFaS/h0Oy3mXLOQ1H5n3cZ2MSEMKQSs9ff8gzeHpWyXpgB1XEKNcUvi
+rVKfaJ/WE5v0rieG89B/YnQTduQnrwmPVuwm4wIDAQABAoIBAQCxKKrC7+DqwKvc
+ybem6Ph8HBeBaNX1HpC5sjVAiKt8IxpFBc1F7VEy97POywkfUp3a/rorKaG2y6i6
+7KoTTOIB8KcDRoIBub4Y3qQV03JWfV3vALtXhAWIGrmhDX8Hux0RnSeJ+8EmewI3
+034+qCkGfOuB7nYy/cJ3IHhD6NfG3Q3FrBrGfsI2TGEeGmPJ2Xi8ZyfbluRb/1Bt
+NesS6pDbRpZ5/IoauLUtITY3bazpzghm2tJNdrJIP7ohaoMF0WYciPyD5xpNlykt
+V8Q2jzNmPYVXuUpi4oPekq4Mg1vd/LPS/JE558Am1LEiXrycelGNrDvJW7hTDLVx
+DLRFuMMxAoGBAMkjupL3mxAfNytXM++WxJbdWPuw/vvAeN60ifFu6RUrMs/aXocn
+4xSunNF58O2aRfSq/B9LJ+pXtmdITV+Bu0Y1XefKtNUNoqIapAbA8gAWUcFSkDRd
+999rh0vWPbx4d3k69iS6xIjVaRcxeuaBbKRWqUcrxDuAydhwTLIRMD1vAoGBAPH4
+quLGkr1MdTeZ3qPAWc9mGelp0LhHukjnLB+nMdI73OH7IlX5or11yr6La/+sTmmQ
+fI+oITLuCyey7VnWBDhrPmWFGA1BmZIVDqjkJJNwyWQO7N27rQEQoNKm5n6Q+boy
+StNKa/ljduYXCjsBndOmF1wSrAwL+u9rQ3x4k9vNAoGAGY5vm1LYofDFar1WvP90
+FRMkxj4T99rZwLpBuKp19RmbCCvfzN51jOAuzrLmuNncP50mEbfT54OjinX2Vsc+
+C0qmltf7qAJmgqBN7QnA9d/gHWcnKXAzGXEpLKqZB4Rq8b1bHwmYBSbQhoDj87vI
+GQ1lzsQx17mia9zA8fMbJQMCgYB0D+2PpuW9rM3QpJp4+wtZAsVNAzddHPKKg2/T
+ovOvvoz9S+M1T+8yZyyfZuqfkTtvQSGuGlwKPMnW+ekFHTWbBj3Ani1iNmP+AOGu
+OvgcTI4c01fkJ2AdUaeCQxHuBYXzPKpNXLYbwgzG4qhCk0zrtxAfVsl1Yc20R0Pw
+kTmCxQKBgQCzd/OOLm7vDEUqYNUNgKlf8I9xM84IwUy+pJP8RaeFDtFj7tVDpY2P
+GXHBXcIBDRPnmBxC7cGHCB3KBWJp11smw2qA0ZgmBIShNm1RDHf/1h0yOxSz2+fB
+bgeEDefxTxoTMgJ1urwl0KX6R9dbv9YWZWJXk2DQj6UwkMEyXpc+kw==
+-----END RSA PRIVATE KEY-----
+`),
+ },
+ }
+ }),
+ Entry("certificate without basic constraint `CA`", func() testCase {
+ // when
+ keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+ return &x509.Certificate{
+ SerialNumber: big.NewInt(0),
+ }
+ })
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert[0]
+ message: "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)"
+ - field: cert[0]
+ message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+`,
+ input: *keyPair,
+ }
+ }),
+ Entry("certificate without key usage extension `keyCertSign`", func() testCase {
+ // when
+ keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+ return &x509.Certificate{
+ SerialNumber: big.NewInt(0),
+ BasicConstraintsValid: true,
+ IsCA: true,
+ }
+ })
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert[0]
+ message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+`,
+ input: *keyPair,
+ }
+ }),
+ Entry("certificate with key usage extension `keyAgreement`", func() testCase {
+ // when
+ keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+ return &x509.Certificate{
+ SerialNumber: big.NewInt(0),
+ BasicConstraintsValid: true,
+ IsCA: true,
+ KeyUsage: x509.KeyUsageCertSign |
+ x509.KeyUsageCRLSign | x509.KeyUsageKeyAgreement,
+ }
+ })
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert[0]
+ message: "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)"
+`,
+ input: *keyPair,
+ }
+ }),
+ Entry("certificate with multiple violations", func() testCase {
+ // when
+ keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+ return &x509.Certificate{
+ SerialNumber: big.NewInt(0),
+ IsCA: false,
+ KeyUsage: x509.KeyUsageDigitalSignature |
+ x509.KeyUsageKeyAgreement | x509.KeyUsageKeyEncipherment,
+ }
+ })
+ // then
+ Expect(err).ToNot(HaveOccurred())
+
+ return testCase{
+ expectedErr: `
+ violations:
+ - field: cert[0]
+ message: "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)"
+ - field: cert[0]
+ message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+ - field: cert[0]
+ message: "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)"
+`,
+ input: *keyPair,
+ }
+ }),
+ )
+})
diff --git a/pkg/plugins/ca/provided/config/provided_ca_config.pb.go b/pkg/plugins/ca/provided/config/provided_ca_config.pb.go
new file mode 100644
index 0000000..46ac283
--- /dev/null
+++ b/pkg/plugins/ca/provided/config/provided_ca_config.pb.go
@@ -0,0 +1,173 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: pkg/plugins/ca/provided/config/provided_ca_config.proto
+
+package config
+
+import (
+ v1alpha1 "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// ProvidedCertificateAuthorityConfig defines configuration for Provided CA
+// plugin
+type ProvidedCertificateAuthorityConfig struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Data source for the certificate of CA
+ Cert *v1alpha1.DataSource `protobuf:"bytes,1,opt,name=cert,proto3" json:"cert,omitempty"`
+ // Data source for the key of CA
+ Key *v1alpha1.DataSource `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+}
+
+func (x *ProvidedCertificateAuthorityConfig) Reset() {
+ *x = ProvidedCertificateAuthorityConfig{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ProvidedCertificateAuthorityConfig) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProvidedCertificateAuthorityConfig) ProtoMessage() {}
+
+func (x *ProvidedCertificateAuthorityConfig) ProtoReflect() protoreflect.Message {
+ mi := &file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProvidedCertificateAuthorityConfig.ProtoReflect.Descriptor instead.
+func (*ProvidedCertificateAuthorityConfig) Descriptor() ([]byte, []int) {
+ return file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ProvidedCertificateAuthorityConfig) GetCert() *v1alpha1.DataSource {
+ if x != nil {
+ return x.Cert
+ }
+ return nil
+}
+
+func (x *ProvidedCertificateAuthorityConfig) GetKey() *v1alpha1.DataSource {
+ if x != nil {
+ return x.Key
+ }
+ return nil
+}
+
+var File_pkg_plugins_ca_provided_config_provided_ca_config_proto protoreflect.FileDescriptor
+
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc = []byte{
+ 0x0a, 0x37, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61,
+ 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x64, 0x75, 0x62, 0x62, 0x6f,
+ 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x63, 0x61, 0x1a, 0x24, 0x61, 0x70, 0x69,
+ 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x73,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44,
+ 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12,
+ 0x33, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64,
+ 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
+ 0x03, 0x6b, 0x65, 0x79, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d,
+ 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70,
+ 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescOnce sync.Once
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData = file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc
+)
+
+func file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescGZIP() []byte {
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescOnce.Do(func() {
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData)
+ })
+ return file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData
+}
+
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes = []interface{}{
+ (*ProvidedCertificateAuthorityConfig)(nil), // 0: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig
+ (*v1alpha1.DataSource)(nil), // 1: dubbo.system.v1alpha1.DataSource
+}
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs = []int32{
+ 1, // 0: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig.cert:type_name -> dubbo.system.v1alpha1.DataSource
+ 1, // 1: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig.key:type_name -> dubbo.system.v1alpha1.DataSource
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_pkg_plugins_ca_provided_config_provided_ca_config_proto_init() }
+func file_pkg_plugins_ca_provided_config_provided_ca_config_proto_init() {
+ if File_pkg_plugins_ca_provided_config_provided_ca_config_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ProvidedCertificateAuthorityConfig); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes,
+ DependencyIndexes: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs,
+ MessageInfos: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes,
+ }.Build()
+ File_pkg_plugins_ca_provided_config_provided_ca_config_proto = out.File
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc = nil
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes = nil
+ file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs = nil
+}
diff --git a/pkg/plugins/ca/provided/config/provided_ca_config.proto b/pkg/plugins/ca/provided/config/provided_ca_config.proto
new file mode 100644
index 0000000..a2846c6
--- /dev/null
+++ b/pkg/plugins/ca/provided/config/provided_ca_config.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package dubbo.plugins.ca;
+
+option go_package = "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/config";
+
+import "api/system/v1alpha1/datasource.proto";
+
+// ProvidedCertificateAuthorityConfig defines configuration for Provided CA
+// plugin
+message ProvidedCertificateAuthorityConfig {
+ // Data source for the certificate of CA
+ dubbo.system.v1alpha1.DataSource cert = 1;
+ // Data source for the key of CA
+ dubbo.system.v1alpha1.DataSource key = 2;
+}
diff --git a/pkg/plugins/ca/provided/manager.go b/pkg/plugins/ca/provided/manager.go
new file mode 100644
index 0000000..a7b48cf
--- /dev/null
+++ b/pkg/plugins/ca/provided/manager.go
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provided
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ ca_issuer "github.com/apache/dubbo-kubernetes/pkg/core/ca/issuer"
+ "github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+type providedCaManager struct {
+ dataSourceLoader datasource.Loader
+}
+
+var _ ca.Manager = &providedCaManager{}
+
+func NewProvidedCaManager(dataSourceLoader datasource.Loader) ca.Manager {
+ return &providedCaManager{
+ dataSourceLoader: dataSourceLoader,
+ }
+}
+
+func (p *providedCaManager) ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error {
+ verr := validators.ValidationError{}
+
+ cfg := &config.ProvidedCertificateAuthorityConfig{}
+ if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+ verr.AddViolation("", "could not convert backend config: "+err.Error())
+ return verr.OrNil()
+ }
+
+ if cfg.GetCert() == nil {
+ verr.AddViolation("cert", "has to be defined")
+ } else {
+ verr.AddError("cert", datasource.Validate(cfg.GetCert()))
+ }
+ if cfg.GetKey() == nil {
+ verr.AddViolation("key", "has to be defined")
+ } else {
+ verr.AddError("key", datasource.Validate(cfg.GetKey()))
+ }
+
+ if !verr.HasViolations() {
+ pair, err := p.getCa(ctx, mesh, backend)
+ if err != nil {
+ verr.AddViolation("cert", err.Error())
+ verr.AddViolation("key", err.Error())
+ } else {
+ verr.AddError("", validateCaCert(pair))
+ }
+ }
+ return verr.OrNil()
+}
+
+func (p *providedCaManager) getCa(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) (ca.KeyPair, error) {
+ cfg := &config.ProvidedCertificateAuthorityConfig{}
+ if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+ return ca.KeyPair{}, errors.Wrap(err, "could not convert backend config to ProvidedCertificateAuthorityConfig")
+ }
+ key, err := p.dataSourceLoader.Load(ctx, mesh, cfg.Key)
+ if err != nil {
+ return ca.KeyPair{}, err
+ }
+ cert, err := p.dataSourceLoader.Load(ctx, mesh, cfg.Cert)
+ if err != nil {
+ return ca.KeyPair{}, err
+ }
+ pair := ca.KeyPair{
+ CertPEM: cert,
+ KeyPEM: key,
+ }
+ return pair, nil
+}
+
+func (p *providedCaManager) EnsureBackends(ctx context.Context, mesh model.Resource, backend []*mesh_proto.CertificateAuthorityBackend) error {
+ return nil // Cert and Key are created by user and pointed in the configuration which is validated first
+}
+
+func (p *providedCaManager) UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error) {
+ cfg := &config.ProvidedCertificateAuthorityConfig{}
+ if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+ return nil, errors.Wrap(err, "could not convert backend config to ProvidedCertificateAuthorityConfig")
+ }
+ var secrets []string
+ if cfg.GetCert().GetSecret() != "" {
+ secrets = append(secrets, cfg.GetCert().GetSecret())
+ }
+ if cfg.GetKey().GetSecret() != "" {
+ secrets = append(secrets, cfg.GetKey().GetSecret())
+ }
+ return secrets, nil
+}
+
+func (p *providedCaManager) GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]ca.Cert, error) {
+ meshCa, err := p.getCa(ctx, mesh, backend)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+ }
+ return []ca.Cert{meshCa.CertPEM}, nil
+}
+
+func (p *providedCaManager) GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (ca.KeyPair, error) {
+ meshCa, err := p.getCa(ctx, mesh, backend)
+ if err != nil {
+ return ca.KeyPair{}, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+ }
+
+ var opts []ca_issuer.CertOptsFn
+ if backend.GetDpCert().GetRotation().GetExpiration() != "" {
+ duration, err := core_mesh.ParseDuration(backend.GetDpCert().GetRotation().Expiration)
+ if err != nil {
+ return ca.KeyPair{}, err
+ }
+ opts = append(opts, ca_issuer.WithExpirationTime(duration))
+ }
+ keyPair, err := ca_issuer.NewWorkloadCert(meshCa, mesh, tags, opts...)
+ if err != nil {
+ return ca.KeyPair{}, errors.Wrapf(err, "failed to generate a Workload Identity cert for tags %q in Mesh %q using backend %q", tags.String(), mesh, backend.Name)
+ }
+ return *keyPair, nil
+}
diff --git a/pkg/plugins/ca/provided/manager_test.go b/pkg/plugins/ca/provided/manager_test.go
new file mode 100644
index 0000000..3458578
--- /dev/null
+++ b/pkg/plugins/ca/provided/manager_test.go
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provided_test
+
+import (
+ "context"
+ "crypto/x509"
+ "encoding/pem"
+ "os"
+ "path/filepath"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "google.golang.org/protobuf/types/known/structpb"
+ "sigs.k8s.io/yaml"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ "github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+ provided_config "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+ "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Provided CA", func() {
+ var caManager core_ca.Manager
+
+ now := time.Now()
+
+ BeforeEach(func() {
+ core.Now = func() time.Time {
+ return now
+ }
+ caManager = provided.NewProvidedCaManager(datasource.NewDataSourceLoader(nil))
+ })
+
+ AfterEach(func() {
+ core.Now = time.Now
+ })
+
+ Context("ValidateBackend", func() {
+ type testCase struct {
+ configYAML string
+ expected string
+ }
+
+ DescribeTable("should Validate invalid config",
+ func(given testCase) {
+ // given
+ str := structpb.Struct{}
+ err := proto.FromYAML([]byte(given.configYAML), &str)
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ verr := caManager.ValidateBackend(context.Background(), "default", &mesh_proto.CertificateAuthorityBackend{
+ Name: "provided-1",
+ Type: "provided",
+ Conf: &str,
+ })
+
+ // then
+ actual, err := yaml.Marshal(verr)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(actual).To(MatchYAML(given.expected))
+ },
+ Entry("empty config", testCase{
+ configYAML: ``,
+ expected: `
+ violations:
+ - field: cert
+ message: has to be defined
+ - field: key
+ message: has to be defined`,
+ }),
+ Entry("config without data source", testCase{
+ configYAML: `
+ cert: {}
+ key: {}`,
+ expected: `
+ violations:
+ - field: cert
+ message: 'data source has to be chosen. Available sources: secret, file, inline'
+ - field: key
+ message: 'data source has to be chosen. Available sources: secret, file, inline'`,
+ }),
+ Entry("config with empty secret", testCase{
+ configYAML: `
+ cert:
+ secret:
+ key:
+ secret:`,
+ expected: `
+ violations:
+ - field: cert
+ message: 'data source has to be chosen. Available sources: secret, file, inline'
+ - field: key
+ message: 'data source has to be chosen. Available sources: secret, file, inline'`,
+ }),
+ Entry("config with empty secret", testCase{
+ configYAML: `
+ cert:
+ file: '/tmp/non-existing-file'
+ key:
+ file: /tmp/non-existing-file`,
+ expected: `
+ violations:
+ - field: cert
+ message: 'could not load data: open /tmp/non-existing-file: no such file or directory'
+ - field: key
+ message: 'could not load data: open /tmp/non-existing-file: no such file or directory'`,
+ }),
+ Entry("config with invalid cert", testCase{
+ configYAML: `
+ cert:
+ inline: dGVzdA==
+ key:
+ inline: dGVzdA==`,
+ expected: `
+ violations:
+ - field: cert
+ message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'`,
+ }),
+ )
+ })
+
+ var backendWithTestCerts *mesh_proto.CertificateAuthorityBackend
+ var backendWithInvalidCerts *mesh_proto.CertificateAuthorityBackend
+
+ BeforeEach(func() {
+ cfg := provided_config.ProvidedCertificateAuthorityConfig{
+ Cert: &system_proto.DataSource{
+ Type: &system_proto.DataSource_File{
+ File: filepath.Join("testdata", "ca.pem"),
+ },
+ },
+ Key: &system_proto.DataSource{
+ Type: &system_proto.DataSource_File{
+ File: filepath.Join("testdata", "ca.key"),
+ },
+ },
+ }
+ str, err := proto.ToStruct(&cfg)
+ Expect(err).ToNot(HaveOccurred())
+
+ backendWithTestCerts = &mesh_proto.CertificateAuthorityBackend{
+ Name: "provided-1",
+ Type: "provided",
+ Conf: str,
+ DpCert: &mesh_proto.CertificateAuthorityBackend_DpCert{
+ Rotation: &mesh_proto.CertificateAuthorityBackend_DpCert_Rotation{
+ Expiration: "1s",
+ },
+ },
+ }
+
+ invalidCfg := provided_config.ProvidedCertificateAuthorityConfig{
+ Cert: &system_proto.DataSource{
+ Type: &system_proto.DataSource_File{
+ File: filepath.Join("testdata", "invalid.pem"),
+ },
+ },
+ Key: &system_proto.DataSource{
+ Type: &system_proto.DataSource_File{
+ File: filepath.Join("testdata", "invalid.key"),
+ },
+ },
+ }
+ invalidStr, err := proto.ToStruct(&invalidCfg)
+ Expect(err).ToNot(HaveOccurred())
+
+ backendWithInvalidCerts = &mesh_proto.CertificateAuthorityBackend{
+ Name: "provided-2",
+ Type: "provided",
+ Conf: invalidStr,
+ }
+ })
+
+ Context("GetRootCert", func() {
+ It("should load return root certs", func() {
+ // given
+ expectedCert, err := os.ReadFile(filepath.Join("testdata", "ca.pem"))
+ Expect(err).ToNot(HaveOccurred())
+
+ // when
+ rootCerts, err := caManager.GetRootCert(context.Background(), "default", backendWithTestCerts)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(rootCerts).To(HaveLen(1))
+ Expect(rootCerts[0]).To(Equal(expectedCert))
+ })
+
+ It("should throw an error on invalid certs", func() {
+ // when
+ _, err := caManager.GetRootCert(context.Background(), "default", backendWithInvalidCerts)
+
+ // then
+ Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "provided-2": could not load data: open testdata/invalid.key: no such file or directory`))
+ })
+ })
+
+ Context("GenerateDataplaneCert", func() {
+ It("should generate dataplane cert", func() {
+ // when
+ tags := map[string]map[string]bool{
+ "dubbo.io/service": {
+ "web": true,
+ "web-api": true,
+ },
+ "version": {
+ "v1": true,
+ },
+ }
+ pair, err := caManager.GenerateDataplaneCert(context.Background(), "default", backendWithTestCerts, tags)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(pair.KeyPEM).ToNot(BeEmpty())
+ Expect(pair.CertPEM).ToNot(BeEmpty())
+
+ // and should generate cert for dataplane with spiffe URI
+ block, _ := pem.Decode(pair.CertPEM)
+ cert, err := x509.ParseCertificate(block.Bytes)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(cert.URIs).To(HaveLen(5))
+ Expect(cert.URIs[0].String()).To(Equal("spiffe://default/web"))
+ Expect(cert.URIs[1].String()).To(Equal("spiffe://default/web-api"))
+ Expect(cert.URIs[2].String()).To(Equal("dubbo://dubbo.io/service/web"))
+ Expect(cert.URIs[3].String()).To(Equal("dubbo://dubbo.io/service/web-api"))
+ Expect(cert.URIs[4].String()).To(Equal("dubbo://version/v1"))
+ Expect(cert.NotAfter).To(Equal(now.UTC().Truncate(time.Second).Add(1 * time.Second))) // time in cert is in UTC and truncated to seconds
+ })
+
+ It("should throw an error on invalid certs", func() {
+ // when
+ _, err := caManager.GenerateDataplaneCert(context.Background(), "default", backendWithInvalidCerts, mesh_proto.MultiValueTagSet{})
+
+ // then
+ Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "provided-2": could not load data: open testdata/invalid.key: no such file or directory`))
+ })
+ })
+
+ Context("UsedSecret", func() {
+ It("should return empty list when no secrets are used", func() {
+ // when
+ secrets, err := caManager.UsedSecrets("default", backendWithTestCerts)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(secrets).To(BeEmpty())
+ })
+
+ It("should return list of secrets", func() {
+ // given
+ backend := &mesh_proto.CertificateAuthorityBackend{
+ Name: "provided-1",
+ Type: "provided",
+ Conf: proto.MustToStruct(&provided_config.ProvidedCertificateAuthorityConfig{
+ Cert: &system_proto.DataSource{
+ Type: &system_proto.DataSource_Secret{
+ Secret: "cert-sec",
+ },
+ },
+ Key: &system_proto.DataSource{
+ Type: &system_proto.DataSource_Secret{
+ Secret: "key-sec",
+ },
+ },
+ }),
+ }
+
+ // when
+ secrets, err := caManager.UsedSecrets("default", backend)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(secrets).To(HaveLen(2))
+ Expect(secrets[0]).To(Equal("cert-sec"))
+ Expect(secrets[1]).To(Equal("key-sec"))
+ })
+ })
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/provided/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/provided/plugin.go
index 987f638..56f16a2 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/provided/plugin.go
@@ -15,25 +15,21 @@
* limitations under the License.
*/
-package dubbo
+package provided
import (
- "archive/zip"
- "bytes"
+ "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.CaPlugin = &plugin{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+ core_plugins.Register(core_plugins.CaProvided, &plugin{})
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p plugin) NewCaManager(context core_plugins.PluginContext, config core_plugins.PluginConfig) (ca.Manager, error) {
+ return NewProvidedCaManager(context.DataSourceLoader()), nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/provided/provided_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/provided/provided_suite_test.go
index 987f638..30b6f69 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/provided/provided_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package provided_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestCaProvided(t *testing.T) {
+ test.RunSpecs(t, "CA Provided Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/ca/provided/testdata/ca.key b/pkg/plugins/ca/provided/testdata/ca.key
new file mode 100644
index 0000000..46e8605
--- /dev/null
+++ b/pkg/plugins/ca/provided/testdata/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0Q6078jpbZfQ5ZzSyY65zEh2GMWmcMLcj2aeHYuClDwONbGY
+yqlIkoVwnF+8zxlShUTQqi6DuKV8VlWZPjD5gzOqIHgGfnQkEsUT4d53qcsRFqDQ
+PVndfyBp5aEJD7yrD1thxdbc8zPVHw5mcRy86BW5OcQy7k7qK/Ih8sYajaa11cjA
+jJSQVlJ+HxUa3utAKZ2YTh8nTjzvvyndNdPBzLY9n3TGQcPerbRgqXFrsAkWfzGA
+yeca7OK25wO9ig/C4Qqcjd3+JrihDpKj2hTntGznSPzQnSMkyw3YxMjYJRPT6jlS
+bvr/LxT2mYuLdxq2J1cIjd1s4pVqFsPhQOMbUQIDAQABAoIBABWf2tFuy859RKtr
+lFYrAEcEO8sGLtg9H8lpiPNvadthdurQ6wgTiKE3UlWqhYI6kVGds6PW2eMwovLf
+OdG4CScsIe3n3GwaIV0bq5nShDg4/BSGH+QotoOe27VX+fL5xgv5nNx1BDfX5bSn
+rasHmPa8wIMcK1SBLzHKgv50xWxfYVUnJEU1UZOBvjyPJxq3EPnm+nPMR2za/6JD
+/9OpLyDAhPfw/btZZy4fhWUvO47S/05ke30hsrc6BvTtw45edKvlZoKrm7iStyKA
+Lq+IvlDQEbr4V0GE5LsABkiii4cUo90yhKXOVM2DDpzeKjcXDLlRfIlGEon8sduI
+Oi/Bcn0CgYEA5UDDyNfgwNRYTcsiSmnhpxLek5OyFkWmbmUuQ2pO0baNxQRsl+vI
+RNC5bqbAjMcuaD25qh6dxciXpL4kDyekQ0I7d4dpQbc2Zh0bDzDfFv94uzbgU6Vc
+UO16GCfD4hsmbUtFHHXCXKFBldwqcgfuD3f95HHrwLwYSNq/AooZq+sCgYEA6XK/
+9RZC8W0U+RF4Pv8YVmY9jRnwxx/8QopfN8X3tasmLZbq0C2bvJ+pzzQZj7eNOPJv
+zKN2WkfNAkNH91bAIBpLLtdSi5ABu0GNSaxKZvKR0KS1C35JBwBnjuQ12+H3j7bE
+tcDql5vECocyYe5QkkdWziFb6KVArRCWa/0UMrMCgYEA2xrnBBGvMRzBuLgocc54
+aGHUcF98mWTACEqTyMLVygEbxezQjPJAWeTTFToyVVoYtHAp/rl8OQfaRw2jEQjm
+KZInm7nDk5czWqz+q6odzbElBmkQRWGJtZ8CTgvTX0prk5GY3/Z8aGOGd2ARASMr
+F4WrFqb6lx4uZ5MJc7fhiTkCgYB1+tE6atrAKrG7wnFz3PYOzDL1OZPu/qI9erLu
+e/VZcbqkta3MMhCP/l0SY7E7abdMfXG1CTOKItlf+GLJhKUqVD+E17tW3xuQ6Gsg
+PZuIdbTtrh/1RAhiKnkrxPfXPVcg4Wx+spWcs1MbQwJabdd4Zedf24oYhFOwYz6y
+Kr/ncwKBgQDVj70UeX0ueiMFi9Qglkl2PaDcpvAWiJfFwPzDQi4Iwi8FPDAy7m6D
+z97kxka4dORTEFNreni4hGXmYWUdrZWD73fuXN3+GGzEAf34yQyRKHO663FZrjm7
+226TBFum3JknVNlDenwYqW+rRJobA1pZJxjFom7nKH0lBzewpgfrmw==
+-----END RSA PRIVATE KEY-----
diff --git a/pkg/plugins/ca/provided/testdata/ca.pem b/pkg/plugins/ca/provided/testdata/ca.pem
new file mode 100644
index 0000000..f3e76ef
--- /dev/null
+++ b/pkg/plugins/ca/provided/testdata/ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAwMQ0wCwYDVQQKEwRLdW1h
+MQ0wCwYDVQQLEwRNZXNoMRAwDgYDVQQDEwdkZWZhdWx0MB4XDTIwMDQyMzA4NDkw
+MloXDTMwMDQyMTA4NDkxMlowMDENMAsGA1UEChMES3VtYTENMAsGA1UECxMETWVz
+aDEQMA4GA1UEAxMHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANEOtO/I6W2X0OWc0smOucxIdhjFpnDC3I9mnh2LgpQ8DjWxmMqpSJKFcJxf
+vM8ZUoVE0Koug7ilfFZVmT4w+YMzqiB4Bn50JBLFE+Hed6nLERag0D1Z3X8gaeWh
+CQ+8qw9bYcXW3PMz1R8OZnEcvOgVuTnEMu5O6ivyIfLGGo2mtdXIwIyUkFZSfh8V
+Gt7rQCmdmE4fJ048778p3TXTwcy2PZ90xkHD3q20YKlxa7AJFn8xgMnnGuzitucD
+vYoPwuEKnI3d/ia4oQ6So9oU57Rs50j80J0jJMsN2MTI2CUT0+o5Um76/y8U9pmL
+i3catidXCI3dbOKVahbD4UDjG1ECAwEAAaNAMD4wDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wGwYDVR0RBBQwEoYQc3BpZmZlOi8vZGVmYXVsdDANBgkq
+hkiG9w0BAQsFAAOCAQEAaWBjvcumO4qnmhdLLeL3OnSQyoeS6lgG9VL/Dm4/3Dlw
+DkxpAQj27rKLCI7f+bACSG8abxvIEySVs6jlvlDnIpRQ07IXRkPm6osjFPsvk6EA
+PG0cJ48UoiICYEVnFssp+AyNBtiyRwK9S6hi/ipa3NBQjjzD1k/xIy+qKDvmOBh+
+WVfQOVdyZHR10Xf/cK5UtozOdq9fqpDfp2b4lw+1lI/CQh128qIPsBhFUhnjNj3+
+Tb2UrWtc+HEPjIxfr3J90ziSIbrhPQ/rJlfGyJuJk4PYME8KbBaXQhG4tYDeG8Hr
+mdFdBVEUtPHLq/dpu0+RYP6zddZEf/PfhTmcC40sSg==
+-----END CERTIFICATE-----
diff --git a/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go b/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go
new file mode 100644
index 0000000..cc8c95f
--- /dev/null
+++ b/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import (
+ "context"
+
+ "github.com/go-logr/logr"
+ "github.com/pkg/errors"
+ kube_core "k8s.io/api/core/v1"
+ kube_apierrs "k8s.io/apimachinery/pkg/api/errors"
+ kube_runtime "k8s.io/apimachinery/pkg/runtime"
+ kube_record "k8s.io/client-go/tools/record"
+ kube_ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/builder"
+ kube_client "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+ "github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+ k8s_common "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+ mesh_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/api/v1alpha1"
+ util_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/util"
+)
+
+// PodStatusReconciler tracks pods status changes and signals dubbo-dp when it has to complete
+// but only when Dubbo isn't using the SidecarContainer feature
+type PodStatusReconciler struct {
+ kube_client.Client
+ kube_record.EventRecorder
+ Scheme *kube_runtime.Scheme
+ ResourceManager manager.ResourceManager
+ Log logr.Logger
+ ResourceConverter k8s_common.Converter
+ EnvoyAdminClient admin.EnvoyAdminClient
+}
+
+func (r *PodStatusReconciler) Reconcile(ctx context.Context, req kube_ctrl.Request) (kube_ctrl.Result, error) {
+ log := r.Log.WithValues("pod", req.NamespacedName)
+
+ // Fetch the Pod instance
+ pod := &kube_core.Pod{}
+ if err := r.Get(ctx, req.NamespacedName, pod); err != nil {
+ if kube_apierrs.IsNotFound(err) {
+ return kube_ctrl.Result{}, nil
+ }
+ log.Error(err, "unable to fetch Pod")
+ return kube_ctrl.Result{}, err
+ }
+
+ dataplane := &mesh_k8s.Dataplane{}
+ if err := r.Get(ctx, req.NamespacedName, dataplane); err != nil {
+ if kube_apierrs.IsNotFound(err) {
+ log.V(1).Info("dataplane not found")
+ return kube_ctrl.Result{}, nil
+ }
+ log.Error(err, "unable to fetch Dataplane")
+ return kube_ctrl.Result{}, err
+ }
+
+ dp := core_mesh.NewDataplaneResource()
+ if err := r.ResourceConverter.ToCoreResource(dataplane, dp); err != nil {
+ converterLog.Error(err, "failed to parse Dataplane", "dataplane", dataplane.Spec)
+ return kube_ctrl.Result{}, err
+ }
+
+ log.Info("sending request to terminate Envoy")
+ if err := r.EnvoyAdminClient.PostQuit(ctx, dp); err != nil {
+ return kube_ctrl.Result{}, errors.Wrap(err, "envoy admin client failed. Most probably the pod is already going down.")
+ }
+ return kube_ctrl.Result{}, nil
+}
+
+func (r *PodStatusReconciler) SetupWithManager(mgr kube_ctrl.Manager) error {
+ return kube_ctrl.NewControllerManagedBy(mgr).
+ For(&kube_core.Pod{}, builder.WithPredicates(
+ onlyUpdates,
+ onlySidecarContainerRunning,
+ )).
+ Complete(r)
+}
+
+var onlyUpdates = predicate.Funcs{
+ CreateFunc: func(event event.CreateEvent) bool {
+ return true // we need it in case of CP restart
+ },
+ DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
+ return false
+ },
+ UpdateFunc: func(updateEvent event.UpdateEvent) bool {
+ return true
+ },
+ GenericFunc: func(genericEvent event.GenericEvent) bool {
+ return false
+ },
+}
+
+var onlySidecarContainerRunning = predicate.NewPredicateFuncs(
+ func(obj kube_client.Object) bool {
+ pod := obj.(*kube_core.Pod)
+ sidecarContainerRunning := false
+ if pod.Spec.RestartPolicy == kube_core.RestartPolicyAlways {
+ return false
+ }
+
+ for _, cs := range pod.Status.ContainerStatuses {
+ if cs.Name == util_k8s.DubboSidecarContainerName {
+ if cs.State.Terminated != nil {
+ return false
+ }
+ sidecarContainerRunning = true
+ } else {
+ switch pod.Spec.RestartPolicy {
+ case kube_core.RestartPolicyNever:
+ if cs.State.Terminated == nil {
+ // at least one non-sidecar container not terminated
+ // no need to tell envoy to quit yet
+ return false
+ }
+ default:
+ if cs.State.Terminated == nil || cs.State.Terminated.ExitCode != 0 {
+ // at least one non-sidecar container not terminated
+ // or did not completed successfully
+ // no need to tell envoy to quit yet
+ return false
+ }
+ }
+ }
+ }
+
+ return sidecarContainerRunning
+ },
+)
diff --git a/pkg/plugins/runtime/k8s/util/names.go b/pkg/plugins/runtime/k8s/util/names.go
index f4ed7f5..f68406c 100644
--- a/pkg/plugins/runtime/k8s/util/names.go
+++ b/pkg/plugins/runtime/k8s/util/names.go
@@ -18,6 +18,6 @@
package util
const (
- dubboSidecarContainerName = "dubbo-sidecar"
+ DubboSidecarContainerName = "dubbo-sidecar"
dubboGatewayContainerName = "dubbo-gateway"
)
diff --git a/pkg/plugins/secrets/k8s/mapper.go b/pkg/plugins/secrets/k8s/mapper.go
new file mode 100644
index 0000000..e2063c5
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/mapper.go
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package k8s
+
+import (
+ "github.com/pkg/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ common_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s"
+ k8s_model "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/model"
+)
+
+// NewKubernetesMapper creates a ResourceMapper that returns the k8s object as is. This is meant to be used when the underlying store is kubernetes
+func NewKubernetesMapper() k8s.ResourceMapperFunc {
+ return func(resource model.Resource, namespace string) (k8s_model.KubernetesObject, error) {
+ res, err := DefaultConverter().ToKubernetesObject(resource)
+ res.TypeMeta = metav1.TypeMeta{
+ APIVersion: "v1",
+ Kind: "Secret",
+ }
+ if err != nil {
+ return nil, err
+ }
+ if namespace != "" {
+ res.SetNamespace(namespace)
+ }
+ return &Secret{Secret: *res}, nil
+ }
+}
+
+// NewInferenceMapper creates a ResourceMapper that infers a k8s resource from the core_model. Extract namespace from the name if necessary.
+// This mostly useful when the underlying store is not kubernetes but you want to show what a kubernetes version of the policy would be like (in global for example).
+func NewInferenceMapper(systemNamespace string) k8s.ResourceMapperFunc {
+ return func(resource model.Resource, namespace string) (k8s_model.KubernetesObject, error) {
+ var rs k8s_model.KubernetesObject
+ switch resource.Descriptor().Name {
+ case secret_model.SecretType:
+ rs = NewSecret(common_k8s.MeshSecretType)
+ rs.SetMesh(resource.GetMeta().GetMesh())
+ case secret_model.GlobalSecretType:
+ rs = NewSecret(common_k8s.GlobalSecretType)
+ default:
+ return nil, errors.New("invalid resource type")
+ }
+ if namespace != "" { // If the user is forcing the namespace accept it.
+ rs.SetNamespace(namespace)
+ } else {
+ rs.SetNamespace(systemNamespace)
+ }
+ rs.SetName(resource.GetMeta().GetName())
+ rs.SetCreationTimestamp(v1.NewTime(resource.GetMeta().GetCreationTime()))
+ rs.SetSpec(resource.GetSpec())
+ return rs, nil
+ }
+}
diff --git a/pkg/plugins/secrets/k8s/plugin.go b/pkg/plugins/secrets/k8s/plugin.go
new file mode 100644
index 0000000..ca18dd4
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/plugin.go
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package k8s
+
+import (
+ "github.com/pkg/errors"
+
+ core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+ k8s_extensions "github.com/apache/dubbo-kubernetes/pkg/plugins/extensions/k8s"
+)
+
+var _ core_plugins.SecretStorePlugin = &plugin{}
+
+type plugin struct{}
+
+func init() {
+ core_plugins.Register(core_plugins.Kubernetes, &plugin{})
+}
+
+func (p *plugin) NewSecretStore(pc core_plugins.PluginContext, _ core_plugins.PluginConfig) (secret_store.SecretStore, error) {
+ mgr, ok := k8s_extensions.FromManagerContext(pc.Extensions())
+ if !ok {
+ return nil, errors.Errorf("k8s controller runtime Manager hasn't been configured")
+ }
+ client, ok := k8s_extensions.FromSecretClientContext(pc.Extensions())
+ if !ok {
+ return nil, errors.Errorf("secret client hasn't been configured")
+ }
+ return NewStore(client, client, mgr.GetScheme(), pc.Config().Store.Kubernetes.SystemNamespace)
+}
diff --git a/pkg/plugins/secrets/k8s/secret.go b/pkg/plugins/secrets/k8s/secret.go
new file mode 100644
index 0000000..4c582f7
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/secret.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package k8s
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+ v1 "k8s.io/api/core/v1"
+ k8s "k8s.io/apimachinery/pkg/apis/meta/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/model"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/metadata"
+)
+
+// Secret is a KubernetesObject for Dubbo's Secret and GlobalSecret.
+// Note that it's not registered in TypeRegistry because we cannot multiply KubernetesObject
+// for a single Spec (both Secret and GlobalSecret has same Spec).
+type Secret struct {
+ v1.Secret
+}
+
+func NewSecret(typ v1.SecretType) *Secret {
+ return &Secret{
+ Secret: v1.Secret{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: v1.SchemeGroupVersion.String(),
+ Kind: "Secret",
+ },
+ Type: typ,
+ },
+ }
+}
+
+var _ model.KubernetesObject = &Secret{}
+
+func (s *Secret) GetObjectMeta() *k8s.ObjectMeta {
+ return &s.ObjectMeta
+}
+
+func (s *Secret) SetObjectMeta(meta *k8s.ObjectMeta) {
+ s.ObjectMeta = *meta
+}
+
+func (s *Secret) GetMesh() string {
+ if mesh, ok := s.ObjectMeta.Labels[metadata.DubboMeshLabel]; ok {
+ return mesh
+ } else {
+ return core_model.DefaultMesh
+ }
+}
+
+func (s *Secret) SetMesh(mesh string) {
+ if s.ObjectMeta.Labels == nil {
+ s.ObjectMeta.Labels = map[string]string{}
+ }
+ s.ObjectMeta.Labels[metadata.DubboMeshLabel] = mesh
+}
+
+func (s *Secret) GetSpec() (core_model.ResourceSpec, error) {
+ bytes, ok := s.Data["value"]
+ if !ok {
+ return nil, nil
+ }
+ return &system_proto.Secret{
+ Data: &wrapperspb.BytesValue{
+ Value: bytes,
+ },
+ }, nil
+}
+
+func (s *Secret) SetSpec(spec core_model.ResourceSpec) {
+ if _, ok := spec.(*system_proto.Secret); !ok {
+ panic(fmt.Sprintf("unexpected protobuf message type %T", spec))
+ }
+ s.Data = map[string][]byte{
+ "value": spec.(*system_proto.Secret).GetData().GetValue(),
+ }
+}
+
+func (s *Secret) GetStatus() (core_model.ResourceStatus, error) {
+ return nil, nil
+}
+
+func (s *Secret) SetStatus(status core_model.ResourceStatus) error {
+ return errors.New("status not supported")
+}
+
+func (s *Secret) Scope() model.Scope {
+ return model.ScopeNamespace
+}
diff --git a/pkg/plugins/secrets/k8s/store.go b/pkg/plugins/secrets/k8s/store.go
new file mode 100644
index 0000000..5cdc316
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/store.go
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package k8s
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/pkg/errors"
+ kube_core "k8s.io/api/core/v1"
+ kube_apierrs "k8s.io/apimachinery/pkg/api/errors"
+ kube_meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ kube_client "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+
+ system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+ secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+ common_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s"
+ "github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/metadata"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ secret_store.SecretStore = &KubernetesStore{}
+
+type KubernetesStore struct {
+ reader kube_client.Reader
+ writer kube_client.Writer
+ scheme *runtime.Scheme
+ secretsConverter Converter
+ // secrets have a special converter, and we need to convert the mesh object since it's the owner
+ resourcesConverter common_k8s.Converter
+ // Namespace to store Secrets in, e.g. namespace where Control Plane is installed to
+ namespace string
+}
+
+func NewStore(reader kube_client.Reader, writer kube_client.Writer, scheme *runtime.Scheme, namespace string) (secret_store.SecretStore, error) {
+ return &KubernetesStore{
+ reader: reader,
+ writer: writer,
+ scheme: scheme,
+ secretsConverter: DefaultConverter(),
+ resourcesConverter: k8s.NewSimpleConverter(),
+ namespace: namespace,
+ }, nil
+}
+
+func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs ...core_store.CreateOptionsFunc) error {
+ opts := core_store.NewCreateOptions(fs...)
+ secret, err := s.secretsConverter.ToKubernetesObject(r)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+ }
+ secret.Namespace = s.namespace
+ secret.Name = opts.Name
+ if r.Descriptor().Scope == core_model.ScopeMesh {
+ setMesh(secret, opts.Mesh, opts.Labels)
+ }
+
+ if opts.Owner != nil {
+ k8sOwner, err := s.resourcesConverter.ToKubernetesObject(opts.Owner)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert core model into k8s counterpart")
+ }
+ if err := controllerutil.SetOwnerReference(k8sOwner, secret, s.scheme); err != nil {
+ return errors.Wrap(err, "failed to set owner reference for object")
+ }
+ }
+
+ if err := s.writer.Create(ctx, secret); err != nil {
+ if kube_apierrs.IsAlreadyExists(err) {
+ return core_store.ErrorResourceAlreadyExists(r.Descriptor().Name, secret.Name, opts.Mesh)
+ }
+ return errors.Wrap(err, "failed to create k8s Secret")
+ }
+ err = s.secretsConverter.ToCoreResource(secret, r)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+ }
+ return nil
+}
+
+func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+ opts := core_store.NewUpdateOptions(fs...)
+
+ secret, err := s.secretsConverter.ToKubernetesObject(r)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+ }
+ secret.Namespace = s.namespace
+ if r.Descriptor().Scope == core_model.ScopeMesh {
+ setMesh(secret, r.GetMeta().GetMesh(), opts.Labels)
+ }
+ if err := s.writer.Update(ctx, secret); err != nil {
+ if kube_apierrs.IsConflict(err) {
+ return core_store.ErrorResourceConflict(r.Descriptor().Name, secret.Name, r.GetMeta().GetMesh())
+ }
+ return errors.Wrap(err, "failed to update k8s Secret")
+ }
+ err = s.secretsConverter.ToCoreResource(secret, r)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+ }
+ return nil
+}
+
+func setMesh(s *kube_core.Secret, mesh string, labels map[string]string) {
+ if labels == nil {
+ labels = map[string]string{}
+ }
+ labels[metadata.DubboMeshLabel] = mesh
+ s.SetLabels(labels)
+}
+
+func (s *KubernetesStore) Delete(ctx context.Context, r core_model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+ opts := core_store.NewDeleteOptions(fs...)
+ if err := s.Get(ctx, r, core_store.GetByKey(opts.Name, opts.Mesh)); err != nil {
+ return errors.Wrap(err, "failed to delete k8s secret")
+ }
+
+ secret, err := s.secretsConverter.ToKubernetesObject(r)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+ }
+ secret.Namespace = s.namespace
+ secret.Name = opts.Name
+
+ if err := s.writer.Delete(ctx, secret); err != nil {
+ return errors.Wrap(err, "failed to delete k8s Secret")
+ }
+ return nil
+}
+
+func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...core_store.GetOptionsFunc) error {
+ opts := core_store.NewGetOptions(fs...)
+ secret := &kube_core.Secret{}
+ if err := s.reader.Get(ctx, kube_client.ObjectKey{Namespace: s.namespace, Name: opts.Name}, secret); err != nil {
+ if kube_apierrs.IsNotFound(err) {
+ return core_store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
+ }
+ return errors.Wrap(err, "failed to get k8s secret")
+ }
+ if err := s.secretsConverter.ToCoreResource(secret, r); err != nil {
+ return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+ }
+ if err := assertFound(r, secret, opts.Name, opts.Mesh); err != nil {
+ return err
+ }
+ return nil
+}
+
+func assertFound(r core_model.Resource, secret *kube_core.Secret, name string, mesh string) error {
+ switch r.Descriptor().Name {
+ case secret_model.SecretType:
+ // secret must match mesh and be a proper type, otherwise return not found
+ if r.GetMeta().GetMesh() != mesh || secret.Type != common_k8s.MeshSecretType {
+ if err := r.SetSpec(&system_proto.Secret{}); err != nil {
+ return err
+ }
+ return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
+ }
+ case secret_model.GlobalSecretType:
+ // secret must be a proper type, otherwise return not found
+ if secret.Type != common_k8s.GlobalSecretType {
+ if err := r.SetSpec(&system_proto.Secret{}); err != nil {
+ return err
+ }
+ return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
+ }
+ }
+ return nil
+}
+
+func (s *KubernetesStore) List(ctx context.Context, rs core_model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+ opts := core_store.NewListOptions(fs...)
+ secrets := &kube_core.SecretList{}
+
+ fields := kube_client.MatchingFields{} // list only Dubbo System secrets
+ labels := kube_client.MatchingLabels{}
+ switch rs.GetItemType() {
+ case secret_model.SecretType:
+ fields = kube_client.MatchingFields{ // list only Dubbo System secrets
+ "type": common_k8s.MeshSecretType,
+ }
+ if opts.Mesh != "" {
+ labels[metadata.DubboMeshLabel] = opts.Mesh
+ }
+ case secret_model.GlobalSecretType:
+ fields = kube_client.MatchingFields{ // list only Dubbo System secrets
+ "type": common_k8s.GlobalSecretType,
+ }
+ }
+ if err := s.reader.List(ctx, secrets, kube_client.InNamespace(s.namespace), labels, fields); err != nil {
+ return errors.Wrap(err, "failed to list k8s Secrets")
+ }
+ if err := s.secretsConverter.ToCoreList(secrets, rs); err != nil {
+ return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+ }
+ return nil
+}
+
+var _ core_model.ResourceMeta = &KubernetesMetaAdapter{}
+
+type KubernetesMetaAdapter struct {
+ kube_meta.ObjectMeta
+ SecretType kube_core.SecretType
+}
+
+func (m *KubernetesMetaAdapter) GetNameExtensions() core_model.ResourceNameExtensions {
+ return common_k8s.ResourceNameExtensions(m.ObjectMeta.Namespace, m.ObjectMeta.Name)
+}
+
+func (m *KubernetesMetaAdapter) GetVersion() string {
+ return m.ObjectMeta.GetResourceVersion()
+}
+
+func (m *KubernetesMetaAdapter) GetMesh() string {
+ if m.SecretType == common_k8s.GlobalSecretType {
+ return ""
+ }
+ mesh, exist := m.Labels[metadata.DubboMeshLabel]
+ if !exist {
+ mesh = core_model.DefaultMesh
+ }
+ return mesh
+}
+
+func (m *KubernetesMetaAdapter) GetCreationTime() time.Time {
+ return m.GetObjectMeta().GetCreationTimestamp().Time
+}
+
+func (m *KubernetesMetaAdapter) GetModificationTime() time.Time {
+ return m.GetObjectMeta().GetCreationTimestamp().Time
+}
+
+type Converter interface {
+ ToKubernetesObject(resource core_model.Resource) (*kube_core.Secret, error)
+ ToCoreResource(secret *kube_core.Secret, out core_model.Resource) error
+ ToCoreList(list *kube_core.SecretList, out core_model.ResourceList) error
+}
+
+func DefaultConverter() Converter {
+ return &SimpleConverter{}
+}
+
+var _ Converter = &SimpleConverter{}
+
+type SimpleConverter struct{}
+
+func (c *SimpleConverter) ToKubernetesObject(r core_model.Resource) (*kube_core.Secret, error) {
+ secret := &kube_core.Secret{}
+ switch r.Descriptor().Name {
+ case secret_model.SecretType:
+ secret.Type = common_k8s.MeshSecretType
+ secret.Data = map[string][]byte{
+ "value": r.(*secret_model.SecretResource).Spec.GetData().GetValue(),
+ }
+ case secret_model.GlobalSecretType:
+ secret.Type = common_k8s.GlobalSecretType
+ secret.Data = map[string][]byte{
+ "value": r.(*secret_model.GlobalSecretResource).Spec.GetData().GetValue(),
+ }
+ default:
+ return nil, errors.Errorf("invalid type %s, expected %s or %s", r.Descriptor().Name, secret_model.SecretType, secret_model.GlobalSecretType)
+ }
+ if r.GetMeta() != nil {
+ if adapter, ok := r.GetMeta().(*KubernetesMetaAdapter); ok {
+ secret.ObjectMeta = adapter.ObjectMeta
+ } else {
+ return nil, fmt.Errorf("meta has unexpected type: %#v", r.GetMeta())
+ }
+ }
+ return secret, nil
+}
+
+func (c *SimpleConverter) ToCoreResource(secret *kube_core.Secret, out core_model.Resource) error {
+ out.SetMeta(&KubernetesMetaAdapter{
+ ObjectMeta: secret.ObjectMeta,
+ SecretType: secret.Type,
+ })
+ if secret.Data != nil {
+ _ = out.SetSpec(&system_proto.Secret{
+ Data: util_proto.Bytes(secret.Data["value"]),
+ })
+ }
+ return nil
+}
+
+func (c *SimpleConverter) ToCoreList(in *kube_core.SecretList, out core_model.ResourceList) error {
+ switch out.GetItemType() {
+ case secret_model.SecretType:
+ secOut := out.(*secret_model.SecretResourceList)
+ secOut.Items = make([]*secret_model.SecretResource, len(in.Items))
+ for i := range in.Items {
+ r := secret_model.NewSecretResource()
+ if err := c.ToCoreResource(&in.Items[i], r); err != nil {
+ return err
+ }
+ secOut.Items[i] = r
+ }
+ case secret_model.GlobalSecretType:
+ secOut := out.(*secret_model.GlobalSecretResourceList)
+ secOut.Items = make([]*secret_model.GlobalSecretResource, len(in.Items))
+ for i := range in.Items {
+ r := secret_model.NewGlobalSecretResource()
+ if err := c.ToCoreResource(&in.Items[i], r); err != nil {
+ return err
+ }
+ secOut.Items[i] = r
+ }
+ default:
+ return errors.Errorf("invalid type %s, expected %s or %s", out.GetItemType(), secret_model.SecretType, secret_model.GlobalSecretType)
+ }
+ return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/secrets/universal/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/secrets/universal/plugin.go
index 987f638..77a8d24 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/secrets/universal/plugin.go
@@ -15,25 +15,21 @@
* limitations under the License.
*/
-package dubbo
+package universal
import (
- "archive/zip"
- "bytes"
+ core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+ secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.SecretStorePlugin = &plugin{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+ core_plugins.Register(core_plugins.Universal, &plugin{})
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p *plugin) NewSecretStore(pc core_plugins.PluginContext, _ core_plugins.PluginConfig) (secret_store.SecretStore, error) {
+ return secret_store.NewSecretStore(pc.ResourceStore().DefaultResourceStore()), nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/access/access.go
index 987f638..4471ca0 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/access/access.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package access
import (
- "archive/zip"
- "bytes"
+ "context"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type DataplaneTokenAccess interface {
+ ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tag map[string][]string, user user.User) error
+ ValidateGenerateZoneIngressToken(ctx context.Context, name string, zone string, user user.User) error
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/access/noop.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/access/noop.go
index 987f638..d573664 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/access/noop.go
@@ -15,25 +15,21 @@
* limitations under the License.
*/
-package dubbo
+package access
import (
- "archive/zip"
- "bytes"
+ "context"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type NoopDpTokenAccess struct{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+var _ DataplaneTokenAccess = NoopDpTokenAccess{}
+
+func (n NoopDpTokenAccess) ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tag map[string][]string, user user.User) error {
+ return nil
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (n NoopDpTokenAccess) ValidateGenerateZoneIngressToken(ctx context.Context, name string, mesh string, user user.User) error {
+ return nil
+}
diff --git a/pkg/tokens/builtin/access/static.go b/pkg/tokens/builtin/access/static.go
new file mode 100644
index 0000000..557b7de
--- /dev/null
+++ b/pkg/tokens/builtin/access/static.go
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticGenerateDataplaneTokenAccess struct {
+ usernames map[string]struct{}
+ groups map[string]struct{}
+}
+
+var _ DataplaneTokenAccess = &staticGenerateDataplaneTokenAccess{}
+
+func NewStaticGenerateDataplaneTokenAccess(cfg config_access.GenerateDPTokenStaticAccessConfig) DataplaneTokenAccess {
+ s := &staticGenerateDataplaneTokenAccess{
+ usernames: make(map[string]struct{}, len(cfg.Users)),
+ groups: make(map[string]struct{}, len(cfg.Groups)),
+ }
+ for _, u := range cfg.Users {
+ s.usernames[u] = struct{}{}
+ }
+ for _, group := range cfg.Groups {
+ s.groups[group] = struct{}{}
+ }
+ return s
+}
+
+func (s *staticGenerateDataplaneTokenAccess) ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tags map[string][]string, user user.User) error {
+ return access.Validate(s.usernames, s.groups, user, "generate dataplane token")
+}
+
+func (s *staticGenerateDataplaneTokenAccess) ValidateGenerateZoneIngressToken(ctx context.Context, name string, zone string, user user.User) error {
+ return access.Validate(s.usernames, s.groups, user, "generate zone ingress token")
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/zone/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/zone/access/access.go
index 987f638..5053ff9 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/zone/access/access.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package access
import (
- "archive/zip"
- "bytes"
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+type ZoneTokenAccess interface {
+ ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/zone/access/noop.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/zone/access/noop.go
index 987f638..13e32cc 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/zone/access/noop.go
@@ -15,25 +15,18 @@
* limitations under the License.
*/
-package dubbo
+package access
import (
- "archive/zip"
- "bytes"
+ "context"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type NoopZoneTokenAccess struct{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+var _ ZoneTokenAccess = NoopZoneTokenAccess{}
+
+func (n NoopZoneTokenAccess) ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error {
+ return nil
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/tokens/builtin/zone/access/static.go b/pkg/tokens/builtin/zone/access/static.go
new file mode 100644
index 0000000..e039873
--- /dev/null
+++ b/pkg/tokens/builtin/zone/access/static.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package access
+
+import (
+ "context"
+ "fmt"
+
+ config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/access"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticZoneTokenAccess struct {
+ usernames map[string]struct{}
+ groups map[string]struct{}
+}
+
+var _ ZoneTokenAccess = &staticZoneTokenAccess{}
+
+func NewStaticZoneTokenAccess(cfg config_access.GenerateZoneTokenStaticAccessConfig) ZoneTokenAccess {
+ s := &staticZoneTokenAccess{
+ usernames: map[string]struct{}{},
+ groups: map[string]struct{}{},
+ }
+ for _, user := range cfg.Users {
+ s.usernames[user] = struct{}{}
+ }
+ for _, group := range cfg.Groups {
+ s.groups[group] = struct{}{}
+ }
+ return s
+}
+
+func (s *staticZoneTokenAccess) ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error {
+ return access.Validate(s.usernames, s.groups, user, fmt.Sprintf("generate zone token for zone '%s'", zone))
+}
diff --git a/pkg/tokens/builtin/zone/token.go b/pkg/tokens/builtin/zone/token.go
new file mode 100644
index 0000000..f664133
--- /dev/null
+++ b/pkg/tokens/builtin/zone/token.go
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package zone
+
+import (
+ "github.com/golang-jwt/jwt/v4"
+
+ core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+)
+
+const (
+ SigningKeyPrefix = "zone-token-signing-key"
+ SigningPublicKeyPrefix = "zone-token-signing-public-key"
+)
+
+var TokenRevocationsGlobalSecretKey = core_model.ResourceKey{
+ Name: "zone-token-revocations",
+ Mesh: core_model.NoMesh,
+}
+
+type ZoneClaims struct {
+ Zone string
+ Scope []string
+ jwt.RegisteredClaims
+}
+
+var _ core_tokens.Claims = &ZoneClaims{}
+
+func (c *ZoneClaims) ID() string {
+ return c.RegisteredClaims.ID
+}
+
+func (c *ZoneClaims) SetRegisteredClaims(claims jwt.RegisteredClaims) {
+ c.RegisteredClaims = claims
+}
diff --git a/pkg/tokens/client.go b/pkg/tokens/client.go
new file mode 100644
index 0000000..255c1e3
--- /dev/null
+++ b/pkg/tokens/client.go
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tokens
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "net/http"
+
+ "github.com/pkg/errors"
+
+ error_types "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors/types"
+ util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type TokenClient struct {
+ client util_http.Client
+ url string
+}
+
+func NewTokenClient(client util_http.Client, entity string) TokenClient {
+ return TokenClient{
+ client: client,
+ url: "/tokens/" + entity,
+ }
+}
+
+func (tc TokenClient) Generate(tokenReq any) (string, error) {
+ reqBytes, err := json.Marshal(tokenReq)
+ if err != nil {
+ return "", errors.Wrap(err, "could not marshal token request to json")
+ }
+ req, err := http.NewRequest(http.MethodPost, tc.url, bytes.NewReader(reqBytes))
+ if err != nil {
+ return "", errors.Wrap(err, "could not construct the request")
+ }
+ req.Header.Set("content-type", "application/json")
+ resp, err := tc.client.Do(req)
+ if err != nil {
+ return "", errors.Wrap(err, "could not execute the request")
+ }
+ defer resp.Body.Close()
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return "", errors.Wrap(err, "could not read a body of the request")
+ }
+ if resp.StatusCode != http.StatusOK {
+ var kumaErr error_types.Error
+ if err := json.Unmarshal(body, &kumaErr); err == nil {
+ if kumaErr.Title != "" {
+ return "", &kumaErr
+ }
+ }
+ return "", errors.Errorf("(%d): %s", resp.StatusCode, body)
+ }
+ return string(body), nil
+}
diff --git a/pkg/xds/context/context.go b/pkg/xds/context/context.go
index 8537db0..88a66f2 100644
--- a/pkg/xds/context/context.go
+++ b/pkg/xds/context/context.go
@@ -19,9 +19,9 @@
import (
"encoding/base64"
-)
-import (
+ "github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
+
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
"github.com/apache/dubbo-kubernetes/pkg/core/xds"
"github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
@@ -42,6 +42,7 @@
type ControlPlaneContext struct {
CLACache envoy.CLACache
Zone string
+ Secrets secrets.Secrets
}
// GlobalContext holds resources that are Global
@@ -121,3 +122,15 @@
}
return nil
}
+
+func (mc *MeshContext) GetTlsReadiness() map[string]bool {
+ tlsReady := map[string]bool{}
+ for serviceName, info := range mc.ServicesInformation {
+ if info != nil {
+ tlsReady[serviceName] = info.TLSReadiness
+ } else {
+ tlsReady[serviceName] = false
+ }
+ }
+ return tlsReady
+}
diff --git a/pkg/xds/context/resources.go b/pkg/xds/context/resources.go
index 7f4ac3d..68b73d5 100644
--- a/pkg/xds/context/resources.go
+++ b/pkg/xds/context/resources.go
@@ -19,9 +19,7 @@
import (
"hash/fnv"
-)
-import (
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
@@ -72,3 +70,11 @@
func (r Resources) Dataplanes() *core_mesh.DataplaneResourceList {
return r.ListOrEmpty(core_mesh.DataplaneType).(*core_mesh.DataplaneResourceList)
}
+
+func (r Resources) OtherMeshes() *core_mesh.MeshResourceList {
+ return r.ListOrEmpty(core_mesh.MeshType).(*core_mesh.MeshResourceList)
+}
+
+func (r Resources) Meshes() *core_mesh.MeshResourceList {
+ return r.ListOrEmpty(core_mesh.MeshType).(*core_mesh.MeshResourceList)
+}
diff --git a/pkg/xds/envoy/clusters/configurers.go b/pkg/xds/envoy/clusters/configurers.go
index 7d14dd9..bb987e0 100644
--- a/pkg/xds/envoy/clusters/configurers.go
+++ b/pkg/xds/envoy/clusters/configurers.go
@@ -18,14 +18,15 @@
package clusters
import (
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
"google.golang.org/protobuf/types/known/wrapperspb"
-)
-import (
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+
v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters/v3"
+
envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
)
@@ -36,6 +37,14 @@
})
}
+func DefaultTimeout() ClusterBuilderOpt {
+ return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+ builder.AddConfigurer(&v3.TimeoutConfigurer{
+ Protocol: core_mesh.ProtocolTCP,
+ })
+ })
+}
+
// ProvidedEndpointCluster sets the cluster with the defined endpoints, this is useful when endpoints are not discovered using EDS, so we don't use EdsCluster
func ProvidedEndpointCluster(hasIPv6 bool, endpoints ...core_xds.Endpoint) ClusterBuilderOpt {
return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
@@ -113,3 +122,37 @@
builder.AddConfigurer(&v3.HttpConfigurer{})
})
}
+
+func ClientSideMTLS(tracker core_xds.SecretsTracker, mesh *core_mesh.MeshResource, upstreamService string, upstreamTLSReady bool, tags []envoy_tags.Tags) ClusterBuilderOpt {
+ return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+ builder.AddConfigurer(&v3.ClientSideMTLSConfigurer{
+ SecretsTracker: tracker,
+ UpstreamMesh: mesh,
+ UpstreamService: upstreamService,
+ LocalMesh: mesh,
+ Tags: tags,
+ UpstreamTLSReady: upstreamTLSReady,
+ })
+ })
+}
+
+func ClientSideTLS(endpoints []core_xds.Endpoint) ClusterBuilderOpt {
+ return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+ builder.AddConfigurer(&v3.ClientSideTLSConfigurer{
+ Endpoints: endpoints,
+ })
+ })
+}
+
+func CrossMeshClientSideMTLS(tracker core_xds.SecretsTracker, localMesh *core_mesh.MeshResource, upstreamMesh *core_mesh.MeshResource, upstreamService string, upstreamTLSReady bool, tags []envoy_tags.Tags) ClusterBuilderOpt {
+ return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+ builder.AddConfigurer(&v3.ClientSideMTLSConfigurer{
+ SecretsTracker: tracker,
+ UpstreamMesh: upstreamMesh,
+ UpstreamService: upstreamService,
+ LocalMesh: localMesh,
+ Tags: tags,
+ UpstreamTLSReady: upstreamTLSReady,
+ })
+ })
+}
diff --git a/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go b/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
index 8006f34..c21cb65 100644
--- a/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
+++ b/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
@@ -19,9 +19,7 @@
import (
envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
-)
-import (
util_xds "github.com/apache/dubbo-kubernetes/pkg/util/xds"
)
diff --git a/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go b/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go
new file mode 100644
index 0000000..36b2798
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package clusters
+
+import (
+ envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ "google.golang.org/protobuf/types/known/structpb"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ envoy_metadata "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/metadata/v3"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+ envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ClientSideMTLSConfigurer struct {
+ SecretsTracker core_xds.SecretsTracker
+ UpstreamMesh *core_mesh.MeshResource
+ UpstreamService string
+ LocalMesh *core_mesh.MeshResource
+ Tags []tags.Tags
+ SNI string
+ UpstreamTLSReady bool
+ VerifyIdentities []string
+}
+
+var _ ClusterConfigurer = &ClientSideMTLSConfigurer{}
+
+func (c *ClientSideMTLSConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+ if !c.UpstreamMesh.MTLSEnabled() || !c.LocalMesh.MTLSEnabled() {
+ return nil
+ }
+ if c.UpstreamMesh.GetEnabledCertificateAuthorityBackend().Mode == mesh_proto.CertificateAuthorityBackend_PERMISSIVE &&
+ !c.UpstreamTLSReady {
+ return nil
+ }
+
+ meshName := c.UpstreamMesh.GetMeta().GetName()
+ // there might be a situation when there are multiple sam tags passed here for example two outbound listeners with the same tags, therefore we need to distinguish between them.
+ distinctTags := tags.DistinctTags(c.Tags)
+ switch {
+ case len(distinctTags) == 0:
+ transportSocket, err := c.createTransportSocket(c.SNI)
+ if err != nil {
+ return err
+ }
+ cluster.TransportSocket = transportSocket
+ case len(distinctTags) == 1:
+ sni := tls.SNIFromTags(c.Tags[0].WithTags("mesh", meshName))
+ transportSocket, err := c.createTransportSocket(sni)
+ if err != nil {
+ return err
+ }
+ cluster.TransportSocket = transportSocket
+ default:
+ for _, tags := range distinctTags {
+ sni := tls.SNIFromTags(tags.WithTags("mesh", meshName))
+ transportSocket, err := c.createTransportSocket(sni)
+ if err != nil {
+ return err
+ }
+ cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, &envoy_cluster.Cluster_TransportSocketMatch{
+ Name: sni,
+ Match: &structpb.Struct{
+ Fields: envoy_metadata.MetadataFields(tags.WithoutTags(mesh_proto.ServiceTag)),
+ },
+ TransportSocket: transportSocket,
+ })
+ }
+ }
+ return nil
+}
+
+func (c *ClientSideMTLSConfigurer) createTransportSocket(sni string) (*envoy_core.TransportSocket, error) {
+ if !c.UpstreamMesh.MTLSEnabled() {
+ return nil, nil
+ }
+
+ ca := c.SecretsTracker.RequestCa(c.UpstreamMesh.GetMeta().GetName())
+ identity := c.SecretsTracker.RequestIdentityCert()
+
+ var verifyIdentities []string
+ if c.VerifyIdentities != nil {
+ verifyIdentities = c.VerifyIdentities
+ }
+ tlsContext, err := envoy_tls.CreateUpstreamTlsContext(identity, ca, c.UpstreamService, sni, verifyIdentities)
+ if err != nil {
+ return nil, err
+ }
+ if tlsContext == nil {
+ return nil, nil
+ }
+ pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return nil, err
+ }
+ transportSocket := &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+ return transportSocket, nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go b/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go
new file mode 100644
index 0000000..4a019a5
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package clusters
+
+import (
+ "github.com/asaskevich/govalidator"
+ envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ "google.golang.org/protobuf/types/known/structpb"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ envoy_metadata "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/metadata/v3"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+ envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ClientSideTLSConfigurer struct {
+ Endpoints []xds.Endpoint
+ SystemCaPath string
+ UseCommonTlsContext bool // used to handle MeshExternalService
+}
+
+var _ ClusterConfigurer = &ClientSideTLSConfigurer{}
+
+func (c *ClientSideTLSConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+ if c.UseCommonTlsContext && len(c.Endpoints) > 0 {
+ ep := c.Endpoints[0]
+ if ep.ExternalService != nil && ep.ExternalService.TLSEnabled {
+ tsm, err := c.createTransportSocketMatch(&ep, false)
+ if err != nil {
+ return err
+ }
+ cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, tsm)
+ }
+ } else {
+ for i, ep := range c.Endpoints {
+ if ep.ExternalService != nil && ep.ExternalService.TLSEnabled {
+ tsm, err := c.createTransportSocketMatch(&c.Endpoints[i], true)
+ if err != nil {
+ return err
+ }
+ cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, tsm)
+ }
+ }
+ }
+
+ return nil
+}
+
+func (c *ClientSideTLSConfigurer) createTransportSocketMatch(ep *xds.Endpoint, withMatch bool) (*envoy_cluster.Cluster_TransportSocketMatch, error) {
+ sni := ep.ExternalService.ServerName
+ if ep.ExternalService.ServerName == "" && govalidator.IsDNSName(ep.Target) {
+ // SNI can only be a hostname, not IP
+ sni = ep.Target
+ }
+
+ tlsContext, err := envoy_tls.UpstreamTlsContextOutsideMesh(
+ c.SystemCaPath,
+ ep.ExternalService.CaCert,
+ ep.ExternalService.ClientCert,
+ ep.ExternalService.ClientKey,
+ ep.ExternalService.AllowRenegotiation,
+ ep.ExternalService.SkipHostnameVerification,
+ ep.ExternalService.FallbackToSystemCa,
+ ep.Target,
+ sni,
+ ep.ExternalService.SANs,
+ ep.ExternalService.MinTlsVersion,
+ ep.ExternalService.MaxTlsVersion,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return nil, err
+ }
+
+ transportSocket := &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+
+ tsm := envoy_cluster.Cluster_TransportSocketMatch{
+ Name: ep.Target,
+ TransportSocket: transportSocket,
+ }
+
+ if withMatch {
+ tsm.Match = &structpb.Struct{
+ Fields: envoy_metadata.MetadataFields(tags.Tags(ep.Tags).WithoutTags(mesh_proto.ServiceTag)),
+ }
+ }
+
+ return &tsm, nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/configurer.go b/pkg/xds/envoy/clusters/v3/configurer.go
index 5a863ca..1031cdf 100644
--- a/pkg/xds/envoy/clusters/v3/configurer.go
+++ b/pkg/xds/envoy/clusters/v3/configurer.go
@@ -19,6 +19,7 @@
import (
envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+ envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
)
// ClusterConfigurer is responsible for configuring a single aspect of the entire Envoy cluster,
@@ -28,6 +29,11 @@
Configure(cluster *envoy_cluster.Cluster) error
}
+type FilterChainConfigurer interface {
+ // Configure configures a single aspect on a given Envoy filter chain.
+ Configure(filterChain *envoy_listener.FilterChain) error
+}
+
// ClusterMustConfigureFunc adapts a configuration function that never
// fails to the ListenerConfigurer interface.
type ClusterMustConfigureFunc func(cluster *envoy_cluster.Cluster)
diff --git a/pkg/xds/envoy/clusters/v3/server_static_mtls.go b/pkg/xds/envoy/clusters/v3/server_static_mtls.go
new file mode 100644
index 0000000..258ca28
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/server_static_mtls.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package clusters
+
+import (
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+ envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+ envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticMTLSConfigurer struct {
+ MTLSCerts core_xds.ServerSideMTLSCerts
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticMTLSConfigurer{}
+
+func (c *ServerSideStaticMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+ tlsContext := tls.StaticDownstreamTlsContextWithValue(&c.MTLSCerts.ServerPair)
+ tlsContext.RequireClientCertificate = util_proto.Bool(true)
+
+ tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+ ValidationContext: &envoy_tls.CertificateValidationContext{
+ TrustedCa: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: c.MTLSCerts.CaPEM,
+ },
+ },
+ MatchTypedSubjectAltNames: []*envoy_tls.SubjectAltNameMatcher{{
+ SanType: envoy_tls.SubjectAltNameMatcher_DNS,
+ Matcher: &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: envoy_admin_tls.ClientCertSAN,
+ },
+ IgnoreCase: false,
+ },
+ }},
+ },
+ }
+
+ pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return err
+ }
+ filterChain.TransportSocket = &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+ return nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/timeout_configurer.go b/pkg/xds/envoy/clusters/v3/timeout_configurer.go
new file mode 100644
index 0000000..fe95603
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/timeout_configurer.go
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package clusters
+
+import (
+ envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_upstream_http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ policies_defaults "github.com/apache/dubbo-kubernetes/pkg/plugins/policies/core/defaults"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+type TimeoutConfigurer struct {
+ Protocol core_mesh.Protocol
+ Conf *mesh_proto.Timeout_Conf
+}
+
+var _ ClusterConfigurer = &TimeoutConfigurer{}
+
+func (t *TimeoutConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+ cluster.ConnectTimeout = util_proto.Duration(t.Conf.GetConnectTimeoutOrDefault(policies_defaults.DefaultConnectTimeout))
+ switch t.Protocol {
+ case core_mesh.ProtocolHTTP, core_mesh.ProtocolHTTP2, core_mesh.ProtocolGRPC:
+ err := UpdateCommonHttpProtocolOptions(cluster, func(options *envoy_upstream_http.HttpProtocolOptions) {
+ if options.CommonHttpProtocolOptions == nil {
+ options.CommonHttpProtocolOptions = &envoy_core.HttpProtocolOptions{}
+ }
+
+ t.setIdleTimeout(options.CommonHttpProtocolOptions)
+ t.setMaxStreamDuration(options.CommonHttpProtocolOptions)
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (t *TimeoutConfigurer) setIdleTimeout(options *envoy_core.HttpProtocolOptions) {
+ options.IdleTimeout = util_proto.Duration(t.Conf.GetHttp().GetIdleTimeout().AsDuration())
+}
+
+func (t *TimeoutConfigurer) setMaxStreamDuration(options *envoy_core.HttpProtocolOptions) {
+ if msd := t.Conf.GetHttp().GetMaxStreamDuration(); msd != nil && msd.AsDuration() != 0 {
+ options.MaxStreamDuration = msd
+ return
+ }
+
+ // backwards compatibility
+ if t.Protocol == core_mesh.ProtocolGRPC {
+ if msd := t.Conf.GetGrpc().GetMaxStreamDuration(); msd != nil && msd.AsDuration() != 0 {
+ options.MaxStreamDuration = util_proto.Duration(msd.AsDuration())
+ }
+ }
+}
diff --git a/pkg/xds/envoy/listeners/filter_chain_configurers.go b/pkg/xds/envoy/listeners/filter_chain_configurers.go
index 50b75f0..16db7f8 100644
--- a/pkg/xds/envoy/listeners/filter_chain_configurers.go
+++ b/pkg/xds/envoy/listeners/filter_chain_configurers.go
@@ -18,19 +18,19 @@
package listeners
import (
- envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
- envoy_extensions_compression_gzip_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
- envoy_extensions_filters_http_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
- envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
-)
-
-import (
+ common_tls "github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners/v3"
envoy_routes "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/routes"
"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+ envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_extensions_compression_gzip_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
+ envoy_extensions_filters_http_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
+ envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
)
func GrpcStats() FilterChainBuilderOpt {
@@ -57,6 +57,28 @@
})
}
+func ServerSideMTLS(mesh *core_mesh.MeshResource, secrets core_xds.SecretsTracker, tlsVersion *common_tls.Version, tlsCiphers common_tls.TlsCiphers) FilterChainBuilderOpt {
+ return AddFilterChainConfigurer(&v3.ServerSideMTLSConfigurer{
+ Mesh: mesh,
+ SecretsTracker: secrets,
+ TlsVersion: tlsVersion,
+ TlsCiphers: tlsCiphers,
+ })
+}
+
+func ServerSideStaticMTLS(mtlsCerts core_xds.ServerSideMTLSCerts) FilterChainBuilderOpt {
+ return AddFilterChainConfigurer(&v3.ServerSideStaticMTLSConfigurer{
+ MTLSCerts: mtlsCerts,
+ })
+}
+
+func ServerSideStaticTLS(tlsCerts core_xds.ServerSideTLSCertPaths) FilterChainBuilderOpt {
+ return AddFilterChainConfigurer(&v3.ServerSideStaticTLSConfigurer{
+ CertPath: tlsCerts.CertPath,
+ KeyPath: tlsCerts.KeyPath,
+ })
+}
+
func HttpConnectionManager(statsName string, forwardClientCertDetails bool) FilterChainBuilderOpt {
return AddFilterChainConfigurer(&v3.HttpConnectionManagerConfigurer{
StatsName: statsName,
diff --git a/pkg/xds/envoy/listeners/listener_configurers.go b/pkg/xds/envoy/listeners/listener_configurers.go
index b5de92f..8e3ef63 100644
--- a/pkg/xds/envoy/listeners/listener_configurers.go
+++ b/pkg/xds/envoy/listeners/listener_configurers.go
@@ -21,11 +21,11 @@
envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
"google.golang.org/protobuf/types/known/wrapperspb"
-)
-import (
mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+
v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners/v3"
)
diff --git a/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go b/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go
new file mode 100644
index 0000000..46725b0
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+
+ common_tls "github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideMTLSConfigurer struct {
+ Mesh *core_mesh.MeshResource
+ SecretsTracker core_xds.SecretsTracker
+ TlsVersion *common_tls.Version
+ TlsCiphers common_tls.TlsCiphers
+}
+
+var _ FilterChainConfigurer = &ServerSideMTLSConfigurer{}
+
+func (c *ServerSideMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+ if !c.Mesh.MTLSEnabled() {
+ return nil
+ }
+ tlsContext, err := tls.CreateDownstreamTlsContext(c.SecretsTracker.RequestCa(c.Mesh.GetMeta().GetName()), c.SecretsTracker.RequestIdentityCert())
+ if err != nil {
+ return err
+ }
+ if tlsContext != nil {
+ pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return err
+ }
+ filterChain.TransportSocket = &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+ }
+ return nil
+}
diff --git a/pkg/xds/envoy/listeners/v3/server_static_mtls.go b/pkg/xds/envoy/listeners/v3/server_static_mtls.go
new file mode 100644
index 0000000..d586061
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_static_mtls.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+ envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+ envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticMTLSConfigurer struct {
+ MTLSCerts core_xds.ServerSideMTLSCerts
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticMTLSConfigurer{}
+
+func (c *ServerSideStaticMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+ tlsContext := tls.StaticDownstreamTlsContextWithValue(&c.MTLSCerts.ServerPair)
+ tlsContext.RequireClientCertificate = util_proto.Bool(true)
+
+ tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+ ValidationContext: &envoy_tls.CertificateValidationContext{
+ TrustedCa: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: c.MTLSCerts.CaPEM,
+ },
+ },
+ MatchTypedSubjectAltNames: []*envoy_tls.SubjectAltNameMatcher{{
+ SanType: envoy_tls.SubjectAltNameMatcher_DNS,
+ Matcher: &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: envoy_admin_tls.ClientCertSAN,
+ },
+ IgnoreCase: false,
+ },
+ }},
+ },
+ }
+
+ pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return err
+ }
+ filterChain.TransportSocket = &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+ return nil
+}
diff --git a/pkg/xds/envoy/listeners/v3/server_static_tls.go b/pkg/xds/envoy/listeners/v3/server_static_tls.go
new file mode 100644
index 0000000..6f1585a
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_static_tls.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticTLSConfigurer struct {
+ CertPath string
+ KeyPath string
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticTLSConfigurer{}
+
+func (c *ServerSideStaticTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+ tlsContext := envoy_tls.StaticDownstreamTlsContextWithPath(c.CertPath, c.KeyPath)
+
+ pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+ if err != nil {
+ return err
+ }
+ filterChain.TransportSocket = &envoy_core.TransportSocket{
+ Name: "envoy.transport_sockets.tls",
+ ConfigType: &envoy_core.TransportSocket_TypedConfig{
+ TypedConfig: pbst,
+ },
+ }
+ return nil
+}
diff --git a/pkg/xds/envoy/secrets.go b/pkg/xds/envoy/secrets.go
new file mode 100644
index 0000000..0a6450f
--- /dev/null
+++ b/pkg/xds/envoy/secrets.go
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package envoy
+
+import (
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+ xds_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+type identityCertRequest struct {
+ meshName string
+}
+
+func (r identityCertRequest) Name() string {
+ return names.GetSecretName(xds_tls.IdentityCertResource, "secret", r.meshName)
+}
+
+type caRequest struct {
+ meshName string
+}
+
+type allInOneCaRequest struct {
+ meshNames []string
+}
+
+func (r caRequest) Name() string {
+ return names.GetSecretName(xds_tls.MeshCaResource, "secret", r.meshName)
+}
+
+func (r caRequest) MeshName() []string {
+ return []string{r.meshName}
+}
+
+func (r allInOneCaRequest) Name() string {
+ return names.GetSecretName(xds_tls.MeshCaResource, "secret", "all")
+}
+
+func (r allInOneCaRequest) MeshName() []string {
+ return r.meshNames
+}
+
+type secretsTracker struct {
+ ownMesh string
+ allMeshes []string
+
+ identity bool
+ meshes map[string]struct{}
+ allInOne bool
+}
+
+func NewSecretsTracker(ownMesh string, allMeshes []string) core_xds.SecretsTracker {
+ return &secretsTracker{
+ ownMesh: ownMesh,
+ allMeshes: allMeshes,
+
+ meshes: map[string]struct{}{},
+ }
+}
+
+func (st *secretsTracker) RequestIdentityCert() core_xds.IdentityCertRequest {
+ st.identity = true
+ return &identityCertRequest{
+ meshName: st.ownMesh,
+ }
+}
+
+func (st *secretsTracker) RequestCa(mesh string) core_xds.CaRequest {
+ st.meshes[mesh] = struct{}{}
+ return &caRequest{
+ meshName: mesh,
+ }
+}
+
+func (st *secretsTracker) RequestAllInOneCa() core_xds.CaRequest {
+ st.allInOne = true
+ return &allInOneCaRequest{
+ meshNames: st.allMeshes,
+ }
+}
+
+func (st *secretsTracker) UsedIdentity() bool {
+ return st.identity
+}
+
+func (st *secretsTracker) UsedCas() map[string]struct{} {
+ return st.meshes
+}
+
+func (st *secretsTracker) UsedAllInOne() bool {
+ return st.allInOne
+}
diff --git a/pkg/xds/envoy/secrets/v3/ca_secret.go b/pkg/xds/envoy/secrets/v3/ca_secret.go
new file mode 100644
index 0000000..5ebaea6
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/ca_secret.go
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ "bytes"
+
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func CreateCaSecret(secret *core_xds.CaSecret, name string) *envoy_auth.Secret {
+ return &envoy_auth.Secret{
+ Name: name,
+ Type: &envoy_auth.Secret_ValidationContext{
+ ValidationContext: &envoy_auth.CertificateValidationContext{
+ TrustedCa: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: bytes.Join(secret.PemCerts, []byte("\n")),
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/pkg/xds/envoy/secrets/v3/identity_secret.go b/pkg/xds/envoy/secrets/v3/identity_secret.go
new file mode 100644
index 0000000..30278c0
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/identity_secret.go
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ "bytes"
+
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func CreateIdentitySecret(secret *core_xds.IdentitySecret, name string) *envoy_auth.Secret {
+ return &envoy_auth.Secret{
+ Name: name,
+ Type: &envoy_auth.Secret_TlsCertificate{
+ TlsCertificate: &envoy_auth.TlsCertificate{
+ CertificateChain: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: bytes.Join(secret.PemCerts, []byte("\n")),
+ },
+ },
+ PrivateKey: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: secret.PemKey,
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/pkg/xds/envoy/secrets/v3/server_certificate.go b/pkg/xds/envoy/secrets/v3/server_certificate.go
new file mode 100644
index 0000000..5ba5864
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/server_certificate.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3
+
+import (
+ "bytes"
+ "encoding/pem"
+ "io"
+
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+)
+
+// NewServerCertificateSecret populates a new Envoy TLS certificate
+// secret containing the given key and chain of certificates.
+func NewServerCertificateSecret(key *pem.Block, certificates []*pem.Block) *envoy_auth.Secret {
+ mustEncode := func(out io.Writer, b *pem.Block) {
+ if err := pem.Encode(out, b); err != nil {
+ panic(err.Error())
+ }
+ }
+
+ keyBytes := &bytes.Buffer{}
+ certificateBytes := &bytes.Buffer{}
+
+ mustEncode(keyBytes, key)
+
+ for _, c := range certificates {
+ mustEncode(certificateBytes, c)
+ certificateBytes.WriteString("\n")
+ }
+
+ return &envoy_auth.Secret{
+ Type: &envoy_auth.Secret_TlsCertificate{
+ TlsCertificate: &envoy_auth.TlsCertificate{
+ CertificateChain: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineString{
+ InlineString: certificateBytes.String(),
+ },
+ },
+ PrivateKey: &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineString{
+ InlineString: keyBytes.String(),
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/pkg/xds/envoy/tls/sni.go b/pkg/xds/envoy/tls/sni.go
new file mode 100644
index 0000000..7c304d6
--- /dev/null
+++ b/pkg/xds/envoy/tls/sni.go
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tls
+
+import (
+ "fmt"
+ "hash/fnv"
+ _ "hash/fnv"
+ "strings"
+
+ "github.com/pkg/errors"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ _ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/util/maps"
+ _ "github.com/apache/dubbo-kubernetes/pkg/util/maps"
+ envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+)
+
+func SNIFromTags(tags envoy_tags.Tags) string {
+ extraTags := tags.WithoutTags(mesh_proto.ServiceTag).String()
+ service := tags[mesh_proto.ServiceTag]
+ if extraTags == "" {
+ return service
+ }
+ return fmt.Sprintf("%s{%s}", service, extraTags)
+}
+
+func TagsFromSNI(sni string) (envoy_tags.Tags, error) {
+ parts := strings.Split(sni, "{")
+ if len(parts) > 2 {
+ return nil, errors.New(fmt.Sprintf("cannot parse tags from sni: %s", sni))
+ }
+ if len(parts) == 1 {
+ return envoy_tags.Tags{mesh_proto.ServiceTag: parts[0]}, nil
+ }
+ cleanedTags := strings.ReplaceAll(parts[1], "}", "")
+ tags, err := envoy_tags.TagsFromString(cleanedTags)
+ if err != nil {
+ return nil, err
+ }
+ tags[mesh_proto.ServiceTag] = parts[0]
+ return tags, nil
+}
+
+const (
+ sniFormatVersion = "a"
+ dnsLabelLimit = 63
+)
+
+func SNIForResource(resName string, meshName string, resType model.ResourceType, port uint32, additionalData map[string]string) string {
+ var mapStrings []string
+ for _, key := range maps.SortedKeys(additionalData) {
+ mapStrings = append(mapStrings, fmt.Sprintf("%s=%s", key, additionalData[key]))
+ }
+
+ hash := fnv.New64a()
+ _, _ = hash.Write([]byte(fmt.Sprintf("%s;%s;%v", resName, meshName, strings.Join(mapStrings, ",")))) // fnv64a does not return error
+ hashBytes := hash.Sum(nil)
+
+ if len(resName) > dnsLabelLimit-1 {
+ resName = resName[:dnsLabelLimit-1] + "x"
+ }
+ if len(meshName) > dnsLabelLimit-1 {
+ meshName = meshName[:dnsLabelLimit-1] + "x"
+ }
+
+ resTypeAbbrv := ""
+ //TODO: multizone service, external service, mesh service
+ switch resType {
+ case "ms":
+ resTypeAbbrv = "ms"
+ case "mes":
+ resTypeAbbrv = "mes"
+ case "mzms":
+ resTypeAbbrv = "mzms"
+ default:
+ panic("resource type not supported for SNI")
+ }
+
+ return fmt.Sprintf("%s%x.%s.%d.%s.%s", sniFormatVersion, hashBytes, resName, port, meshName, resTypeAbbrv)
+}
diff --git a/pkg/xds/envoy/tls/sni_test.go b/pkg/xds/envoy/tls/sni_test.go
new file mode 100644
index 0000000..7c45f40
--- /dev/null
+++ b/pkg/xds/envoy/tls/sni_test.go
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tls_test
+
+import (
+ "fmt"
+
+ "github.com/asaskevich/govalidator"
+ "github.com/google/uuid"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ //meshmzservice_api "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1"
+ //meshservice_api "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/meshservice/api/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+var _ = Describe("SNI", func() {
+ It("should convert tags to SNI", func() {
+ // given
+ tags := map[string]string{
+ "dubbo.io/service": "backend",
+ "version": "v1",
+ "env": "prod",
+ "region": "eu",
+ "app": "backend-app",
+ }
+ expected := "backend{app=backend-app,env=prod,region=eu,version=v1}"
+
+ // when
+ actual := tls.SNIFromTags(tags)
+
+ // then
+ Expect(actual).To(Equal(expected))
+ })
+
+ It("should convert tags to SNI with only service name", func() {
+ // given
+ tags := map[string]string{
+ "dubbo.io/service": "backend",
+ }
+ expected := "backend"
+
+ // when
+ actual := tls.SNIFromTags(tags)
+
+ // then
+ Expect(actual).To(Equal(expected))
+ })
+
+ It("should convert SNI to tags", func() {
+ // given
+ sni := "backend{app=backend-app,env=prod,region=eu,version=v1}"
+ expectedTags := envoy_tags.Tags{
+ "dubbo.io/service": "backend",
+ "version": "v1",
+ "env": "prod",
+ "region": "eu",
+ "app": "backend-app",
+ }
+
+ // when
+ tags, err := tls.TagsFromSNI(sni)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(tags).To(Equal(expectedTags))
+ })
+
+ It("should convert SNI to tags with only service name", func() {
+ // given
+ sni := "backend"
+ expectedTags := envoy_tags.Tags{
+ "dubbo.io/service": "backend",
+ }
+
+ // when
+ tags, err := tls.TagsFromSNI(sni)
+
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ Expect(tags).To(Equal(expectedTags))
+ })
+
+ DescribeTable("should fail when converting SNI to tags", func(sni string, errorMessage string) {
+ // when
+ _, err := tls.TagsFromSNI(sni)
+
+ // then
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal(errorMessage))
+ },
+ Entry("broken tags", "backend{", "invalid format of tags, pairs should be separated by , and key should be separated from value by ="),
+ Entry("to many separators", "backend{mesh=default{mesh", "cannot parse tags from sni: backend{mesh=default{mesh"),
+ )
+
+ type testCase struct {
+ resName string
+ meshName string
+ resType model.ResourceType
+ port uint32
+ additionalData map[string]string
+ expected string
+ }
+ DescribeTable("should convert SNI for resource",
+ func(given testCase) {
+ sni := tls.SNIForResource(
+ given.resName,
+ given.meshName,
+ given.resType,
+ given.port,
+ given.additionalData,
+ )
+
+ Expect(sni).To(Equal(given.expected))
+ Expect(govalidator.IsDNSName(sni)).To(BeTrue())
+ },
+ Entry("simple", testCase{
+ resName: "backend",
+ meshName: "demo",
+ resType: "ms",
+ port: 8080,
+ additionalData: nil,
+ expected: "ae10a8071b8a8eeb8.backend.8080.demo.ms",
+ }),
+ Entry("simple subset", testCase{
+ resName: "backend",
+ meshName: "demo",
+ resType: "ms",
+ port: 8080,
+ additionalData: map[string]string{
+ "x": "a",
+ },
+ expected: "a333a125865a97632.backend.8080.demo.ms",
+ }),
+ Entry("going over limit", testCase{
+ resName: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-qwe",
+ meshName: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-qwe",
+ resType: "ms",
+ port: 8080,
+ additionalData: map[string]string{
+ "x": "a",
+ },
+ expected: "a5b91d8a08567bf09.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax.8080.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbx.ms",
+ }),
+ Entry("mesh multizone service", testCase{
+ resName: "backend",
+ meshName: "demo",
+ resType: "mzms",
+ port: 8080,
+ additionalData: nil,
+ expected: "ae10a8071b8a8eeb8.backend.8080.demo.mzms",
+ }),
+ )
+
+ It("SNI hash does not easily collide of the same services with different tags", func() {
+ snis := map[string]struct{}{}
+ for i := 0; i < 100_000; i++ {
+ sni := tls.SNIForResource("backend", "demo", "ms", 8080, map[string]string{
+ "version": fmt.Sprintf("%d", i),
+ })
+ _, ok := snis[sni]
+ Expect(ok).To(BeFalse())
+ snis[sni] = struct{}{}
+ }
+ })
+
+ It("SNI hash does not easily collide of the services with very long names", func() {
+ snis := map[string]struct{}{}
+ serviceName := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ for i := 0; i < 100_000; i++ {
+ sni := tls.SNIForResource(serviceName+uuid.New().String(), "demo", "ms", 8080, nil)
+ _, ok := snis[sni]
+ Expect(ok).To(BeFalse())
+ snis[sni] = struct{}{}
+ }
+ })
+})
diff --git a/pkg/xds/envoy/tls/tls.go b/pkg/xds/envoy/tls/tls.go
new file mode 100644
index 0000000..80deb17
--- /dev/null
+++ b/pkg/xds/envoy/tls/tls.go
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tls
+
+import (
+ "fmt"
+)
+
+const (
+ MeshCaResource = "mesh_ca"
+ IdentityCertResource = "identity_cert"
+ CpValidationCtx = "cp_validation_ctx"
+)
+
+// DubbboALPNProtocols are set for UpstreamTlsContext to show that mTLS is created by mesh.
+// On the inbound side we have to distinguish Dubbo mTLS and application TLS to properly
+// support PERMISSIVE mode
+var DubboALPNProtocols = []string{"dubbo"}
+
+func MeshSpiffeIDPrefix(mesh string) string {
+ return fmt.Sprintf("spiffe://%s/", mesh)
+}
+
+func ServiceSpiffeID(mesh string, service string) string {
+ return fmt.Sprintf("spiffe://%s/%s", mesh, service)
+}
+
+func DubboID(tagName, tagValue string) string {
+ return fmt.Sprintf("dubbo://%s/%s", tagName, tagValue)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/envoy/tls/tls_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/envoy/tls/tls_suite_test.go
index 987f638..dc6bff8 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/envoy/tls/tls_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package tls_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func Test(t *testing.T) {
+ test.RunSpecs(t, "Envoy TLS Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/envoy/tls/v3/tls.go b/pkg/xds/envoy/tls/v3/tls.go
new file mode 100644
index 0000000..bb19457
--- /dev/null
+++ b/pkg/xds/envoy/tls/v3/tls.go
@@ -0,0 +1,294 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v3
+
+import (
+ envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+ envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ "github.com/apache/dubbo-kubernetes/pkg/tls"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ xds_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+// CreateDownstreamTlsContext creates DownstreamTlsContext for incoming connections
+// It verifies that incoming connection has TLS certificate signed by Mesh CA with URI SAN of prefix spiffe://{mesh_name}/
+// It secures inbound listener with certificate of "identity_cert" that will be received from the SDS (it contains URI SANs of all inbounds).
+func CreateDownstreamTlsContext(downstreamMesh core_xds.CaRequest, mesh core_xds.IdentityCertRequest) (*envoy_tls.DownstreamTlsContext, error) {
+ var validationSANMatchers []*envoy_tls.SubjectAltNameMatcher
+ meshNames := downstreamMesh.MeshName()
+ for _, meshName := range meshNames {
+ validationSANMatchers = append(validationSANMatchers, MeshSpiffeIDPrefixMatcher(meshName))
+ }
+
+ commonTlsContext := createCommonTlsContext(mesh, downstreamMesh, validationSANMatchers)
+ return &envoy_tls.DownstreamTlsContext{
+ CommonTlsContext: commonTlsContext,
+ RequireClientCertificate: util_proto.Bool(true),
+ }, nil
+}
+
+// CreateUpstreamTlsContext creates UpstreamTlsContext for outgoing connections
+// It verifies that the upstream server has TLS certificate signed by Mesh CA with URI SAN of spiffe://{mesh_name}/{upstream_service}
+// The downstream client exposes for the upstream server cert with multiple URI SANs, which means that if DP has inbound with services "web" and "web-api" and communicates with "backend"
+// the upstream server ("backend") will see that DP with TLS certificate of URIs of "web" and "web-api".
+// There is no way to correlate incoming request to "web" or "web-api" with outgoing request to "backend" to expose only one URI SAN.
+//
+// Pass "*" for upstreamService to validate that upstream service is a service that is part of the mesh (but not specific one)
+func CreateUpstreamTlsContext(mesh core_xds.IdentityCertRequest, upstreamMesh core_xds.CaRequest, upstreamService string, sni string, verifyIdentities []string) (*envoy_tls.UpstreamTlsContext, error) {
+ var validationSANMatchers []*envoy_tls.SubjectAltNameMatcher
+ meshNames := upstreamMesh.MeshName()
+ for _, meshName := range meshNames {
+ if upstreamService == "*" {
+ if len(verifyIdentities) == 0 {
+ validationSANMatchers = append(validationSANMatchers, MeshSpiffeIDPrefixMatcher(meshName))
+ }
+ for _, identity := range verifyIdentities {
+ stringMatcher := ServiceSpiffeIDMatcher(meshName, identity)
+ matcher := &envoy_tls.SubjectAltNameMatcher{
+ SanType: envoy_tls.SubjectAltNameMatcher_URI,
+ Matcher: stringMatcher,
+ }
+ validationSANMatchers = append(validationSANMatchers, matcher)
+ }
+ } else {
+ stringMatcher := ServiceSpiffeIDMatcher(meshName, upstreamService)
+ matcher := &envoy_tls.SubjectAltNameMatcher{
+ SanType: envoy_tls.SubjectAltNameMatcher_URI,
+ Matcher: stringMatcher,
+ }
+ validationSANMatchers = append(validationSANMatchers, matcher)
+ }
+ }
+ commonTlsContext := createCommonTlsContext(mesh, upstreamMesh, validationSANMatchers)
+ commonTlsContext.AlpnProtocols = xds_tls.DubboALPNProtocols
+ return &envoy_tls.UpstreamTlsContext{
+ CommonTlsContext: commonTlsContext,
+ Sni: sni,
+ }, nil
+}
+
+func createCommonTlsContext(ownMesh core_xds.IdentityCertRequest, targetMeshCa core_xds.CaRequest, matchers []*envoy_tls.SubjectAltNameMatcher) *envoy_tls.CommonTlsContext {
+ meshCaSecret := NewSecretConfigSource(targetMeshCa.Name())
+ identitySecret := NewSecretConfigSource(ownMesh.Name())
+
+ return &envoy_tls.CommonTlsContext{
+ ValidationContextType: &envoy_tls.CommonTlsContext_CombinedValidationContext{
+ CombinedValidationContext: &envoy_tls.CommonTlsContext_CombinedCertificateValidationContext{
+ DefaultValidationContext: &envoy_tls.CertificateValidationContext{
+ MatchTypedSubjectAltNames: matchers,
+ },
+ ValidationContextSdsSecretConfig: meshCaSecret,
+ },
+ },
+ TlsCertificateSdsSecretConfigs: []*envoy_tls.SdsSecretConfig{
+ identitySecret,
+ },
+ }
+}
+
+func NewSecretConfigSource(secretName string) *envoy_tls.SdsSecretConfig {
+ return &envoy_tls.SdsSecretConfig{
+ Name: secretName,
+ SdsConfig: &envoy_core.ConfigSource{
+ ResourceApiVersion: envoy_core.ApiVersion_V3,
+ ConfigSourceSpecifier: &envoy_core.ConfigSource_Ads{},
+ },
+ }
+}
+
+func UpstreamTlsContextOutsideMesh(systemCaPath string, ca, cert, key []byte, allowRenegotiation, skipHostnameVerification, fallbackToSystemCa bool, hostname, sni string, sans []core_xds.SAN, minTlsVersion, maxTlsVersion *core_xds.TlsVersion) (*envoy_tls.UpstreamTlsContext, error) {
+ tlsContext := &envoy_tls.UpstreamTlsContext{
+ AllowRenegotiation: allowRenegotiation,
+ Sni: sni,
+ }
+ if cert != nil && key != nil {
+ tlsContext.CommonTlsContext = &envoy_tls.CommonTlsContext{
+ TlsCertificates: []*envoy_tls.TlsCertificate{
+ {
+ CertificateChain: dataSourceFromBytes(cert),
+ PrivateKey: dataSourceFromBytes(key),
+ },
+ },
+ }
+ }
+
+ if ca != nil || (fallbackToSystemCa && systemCaPath != "") {
+ if tlsContext.CommonTlsContext == nil {
+ tlsContext.CommonTlsContext = &envoy_tls.CommonTlsContext{}
+ }
+ var matchNames []*envoy_tls.SubjectAltNameMatcher
+ if !skipHostnameVerification {
+ subjectAltNameMatch := hostname
+ if len(sni) > 0 {
+ subjectAltNameMatch = sni
+ }
+ for _, typ := range []envoy_tls.SubjectAltNameMatcher_SanType{
+ envoy_tls.SubjectAltNameMatcher_DNS,
+ envoy_tls.SubjectAltNameMatcher_IP_ADDRESS,
+ } {
+ matchNames = append(matchNames, &envoy_tls.SubjectAltNameMatcher{
+ SanType: typ,
+ Matcher: &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: subjectAltNameMatch,
+ },
+ },
+ })
+ }
+ for _, typ := range []envoy_tls.SubjectAltNameMatcher_SanType{
+ envoy_tls.SubjectAltNameMatcher_DNS,
+ envoy_tls.SubjectAltNameMatcher_IP_ADDRESS,
+ } {
+ for _, san := range sans {
+ matcher := &envoy_tls.SubjectAltNameMatcher{
+ SanType: typ,
+ }
+ switch san.MatchType {
+ case core_xds.SANMatchExact:
+ matcher.Matcher = &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: san.Value,
+ },
+ }
+ case core_xds.SANMatchPrefix:
+ matcher.Matcher = &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Prefix{
+ Prefix: san.Value,
+ },
+ }
+ }
+ matchNames = append(matchNames, matcher)
+ }
+ }
+ }
+
+ var trustedCa *envoy_core.DataSource
+ if ca == nil {
+ trustedCa = &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_Filename{
+ Filename: systemCaPath,
+ },
+ }
+ } else {
+ trustedCa = dataSourceFromBytes(ca)
+ }
+
+ tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+ ValidationContext: &envoy_tls.CertificateValidationContext{
+ TrustedCa: trustedCa,
+ MatchTypedSubjectAltNames: matchNames,
+ },
+ }
+
+ if minTlsVersion != nil {
+ tlsContext.CommonTlsContext.TlsParams = &envoy_tls.TlsParameters{
+ TlsMinimumProtocolVersion: envoy_tls.TlsParameters_TlsProtocol(*minTlsVersion),
+ }
+ }
+ if maxTlsVersion != nil {
+ if tlsContext.CommonTlsContext.TlsParams == nil {
+ tlsContext.CommonTlsContext.TlsParams = &envoy_tls.TlsParameters{
+ TlsMaximumProtocolVersion: envoy_tls.TlsParameters_TlsProtocol(*maxTlsVersion),
+ }
+ } else {
+ tlsContext.CommonTlsContext.TlsParams.TlsMaximumProtocolVersion = envoy_tls.TlsParameters_TlsProtocol(*maxTlsVersion)
+ }
+ }
+ }
+ return tlsContext, nil
+}
+
+func dataSourceFromBytes(bytes []byte) *envoy_core.DataSource {
+ return &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: bytes,
+ },
+ }
+}
+
+func MeshSpiffeIDPrefixMatcher(mesh string) *envoy_tls.SubjectAltNameMatcher {
+ stringMatcher := &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Prefix{
+ Prefix: xds_tls.MeshSpiffeIDPrefix(mesh),
+ },
+ }
+
+ return &envoy_tls.SubjectAltNameMatcher{
+ SanType: envoy_tls.SubjectAltNameMatcher_URI,
+ Matcher: stringMatcher,
+ }
+}
+
+// TODO:RBAC Control
+func ServiceSpiffeIDMatcher(mesh string, service string) *envoy_type_matcher.StringMatcher {
+ return &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: xds_tls.ServiceSpiffeID(mesh, service),
+ },
+ }
+}
+
+func DubboIDMatcher(tagName, tagValue string) *envoy_type_matcher.StringMatcher {
+ return &envoy_type_matcher.StringMatcher{
+ MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+ Exact: xds_tls.DubboID(tagName, tagValue),
+ },
+ }
+}
+
+func StaticDownstreamTlsContextWithPath(certPath, keyPath string) *envoy_tls.DownstreamTlsContext {
+ cert := &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_Filename{
+ Filename: certPath,
+ },
+ }
+ key := &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_Filename{
+ Filename: keyPath,
+ },
+ }
+ return staticDownstreamTlsContext(cert, key)
+}
+
+func StaticDownstreamTlsContextWithValue(keyPair *tls.KeyPair) *envoy_tls.DownstreamTlsContext {
+ cert := &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: keyPair.CertPEM,
+ },
+ }
+ key := &envoy_core.DataSource{
+ Specifier: &envoy_core.DataSource_InlineBytes{
+ InlineBytes: keyPair.KeyPEM,
+ },
+ }
+ return staticDownstreamTlsContext(cert, key)
+}
+
+func staticDownstreamTlsContext(cert *envoy_core.DataSource, key *envoy_core.DataSource) *envoy_tls.DownstreamTlsContext {
+ return &envoy_tls.DownstreamTlsContext{
+ CommonTlsContext: &envoy_tls.CommonTlsContext{
+ TlsCertificates: []*envoy_tls.TlsCertificate{
+ {
+ CertificateChain: cert,
+ PrivateKey: key,
+ },
+ },
+ },
+ }
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/envoy/tls/v3/tls_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/envoy/tls/v3/tls_suite_test.go
index 987f638..86c672e 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/envoy/tls/v3/tls_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package v3_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestTLS(t *testing.T) {
+ test.RunSpecs(t, "Envoy TLS v3 Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/envoy/tls/v3/tls_test.go b/pkg/xds/envoy/tls/v3/tls_test.go
new file mode 100644
index 0000000..ce22b99
--- /dev/null
+++ b/pkg/xds/envoy/tls/v3/tls_test.go
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package v3_test
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ test_model "github.com/apache/dubbo-kubernetes/pkg/test/resources/model"
+ util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+ v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type caRequest struct {
+ mesh string
+}
+
+func (r *caRequest) MeshName() []string {
+ return []string{r.mesh}
+}
+
+func (r *caRequest) Name() string {
+ return "mesh_ca:secret:" + r.mesh
+}
+
+type identityRequest struct {
+ mesh string
+}
+
+func (r *identityRequest) Name() string {
+ return "identity_cert:secret:" + r.mesh
+}
+
+var _ = Describe("CreateDownstreamTlsContext()", func() {
+ Context("when mTLS is enabled on a given Mesh", func() {
+ type testCase struct {
+ expected string
+ }
+
+ DescribeTable("should generate proper Envoy config",
+ func(given testCase) {
+ // given
+ mesh := &core_mesh.MeshResource{
+ Meta: &test_model.ResourceMeta{
+ Name: "default",
+ },
+ Spec: &mesh_proto.Mesh{
+ Mtls: &mesh_proto.Mesh_Mtls{
+ EnabledBackend: "builtin",
+ Backends: []*mesh_proto.CertificateAuthorityBackend{
+ {
+ Name: "builtin",
+ Type: "builtin",
+ },
+ },
+ },
+ },
+ }
+
+ // when
+ snippet, err := v3.CreateDownstreamTlsContext(
+ &caRequest{mesh: mesh.GetMeta().GetName()},
+ &identityRequest{mesh: mesh.GetMeta().GetName()},
+ )
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ // when
+ actual, err := util_proto.ToYAML(snippet)
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ // and
+ Expect(actual).To(MatchYAML(given.expected))
+ },
+ Entry("metadata is `nil`", testCase{
+ expected: `
+ commonTlsContext:
+ combinedValidationContext:
+ defaultValidationContext:
+ matchTypedSubjectAltNames:
+ - matcher:
+ prefix: spiffe://default/
+ sanType: URI
+ validationContextSdsSecretConfig:
+ name: mesh_ca:secret:default
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ tlsCertificateSdsSecretConfigs:
+ - name: identity_cert:secret:default
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ requireClientCertificate: true`,
+ }),
+ )
+ })
+})
+
+var _ = Describe("CreateUpstreamTlsContext()", func() {
+ Context("when mTLS is enabled on a given Mesh", func() {
+ type testCase struct {
+ upstreamService string
+ expected string
+ }
+
+ DescribeTable("should generate proper Envoy config",
+ func(given testCase) {
+ // given
+ mesh := "default"
+
+ // when
+ snippet, err := v3.CreateUpstreamTlsContext(
+ &identityRequest{mesh: mesh},
+ &caRequest{mesh: mesh},
+ given.upstreamService,
+ "",
+ nil,
+ )
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ // when
+ actual, err := util_proto.ToYAML(snippet)
+ // then
+ Expect(err).ToNot(HaveOccurred())
+ // and
+ Expect(actual).To(MatchYAML(given.expected))
+ },
+ Entry("metadata is `nil`", testCase{
+ upstreamService: "backend",
+ expected: `
+ commonTlsContext:
+ alpnProtocols:
+ - dubbo
+ combinedValidationContext:
+ defaultValidationContext:
+ matchTypedSubjectAltNames:
+ - matcher:
+ exact: spiffe://default/backend
+ sanType: URI
+ validationContextSdsSecretConfig:
+ name: mesh_ca:secret:default
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ tlsCertificateSdsSecretConfigs:
+ - name: identity_cert:secret:default
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3`,
+ }),
+ )
+ })
+})
diff --git a/pkg/xds/generator/admin_proxy_generator.go b/pkg/xds/generator/admin_proxy_generator.go
new file mode 100644
index 0000000..e05cc69
--- /dev/null
+++ b/pkg/xds/generator/admin_proxy_generator.go
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package generator
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/asaskevich/govalidator"
+ "github.com/pkg/errors"
+
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ util_maps "github.com/apache/dubbo-kubernetes/pkg/util/maps"
+ xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+ envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+ envoy_clusters "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters"
+ envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+ envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+)
+
+// OriginAdmin is a marker to indicate by which ProxyGenerator resources were generated.
+const OriginAdmin = "admin"
+
+var staticEndpointPaths = []*envoy_common.StaticEndpointPath{
+ {
+ Path: "/ready",
+ RewritePath: "/ready",
+ },
+}
+
+var staticTlsEndpointPaths = []*envoy_common.StaticEndpointPath{
+ {
+ Path: "/",
+ RewritePath: "/",
+ },
+}
+
+// AdminProxyGenerator generates resources to expose some endpoints of Admin API on public interface.
+// By default, Admin API is exposed only on loopback interface because of security reasons.
+type AdminProxyGenerator struct{}
+
+var adminAddressAllowedValues = map[string]struct{}{
+ "127.0.0.1": {},
+ "0.0.0.0": {},
+ "::1": {},
+ "::": {},
+ "": {},
+}
+
+func (g AdminProxyGenerator) Generator(ctx context.Context, _ *core_xds.ResourceSet, xdsCtx xds_context.Context, proxy *core_xds.Proxy) (*core_xds.ResourceSet, error) {
+ if proxy.Metadata.GetAdminPort() == 0 {
+ // It's not possible to export Admin endpoints if Envoy Admin API has not been enabled on that dataplane.
+ return nil, nil
+ }
+
+ adminPort := proxy.Metadata.GetAdminPort()
+ // We assume that Admin API must be available on a loopback interface (while users
+ // can override the default value `127.0.0.1` in the Bootstrap Server section of `dubbo-cp` config,
+ // the only reasonable alternatives are `::1`, `0.0.0.0` or `::`).
+ // In contrast to `AdminPort`, we shouldn't trust `AdminAddress` from the Envoy node metadata
+ // since it would allow a malicious user to manipulate that value and use Prometheus endpoint
+ // as a gateway to another host.
+ envoyAdminClusterName := envoy_names.GetEnvoyAdminClusterName()
+ adminAddress := proxy.Metadata.GetAdminAddress()
+ if _, ok := adminAddressAllowedValues[adminAddress]; !ok {
+ var allowedAddresses []string
+ for _, address := range util_maps.SortedKeys(adminAddressAllowedValues) {
+ allowedAddresses = append(allowedAddresses, fmt.Sprintf(`"%s"`, address))
+ }
+ return nil, errors.Errorf("envoy admin cluster is not allowed to have addresses other than %s", strings.Join(allowedAddresses, ", "))
+ }
+ switch adminAddress {
+ case "", "0.0.0.0":
+ adminAddress = "127.0.0.1"
+ case "::":
+ adminAddress = "::1"
+ }
+ cluster, err := envoy_clusters.NewClusterBuilder(proxy.APIVersion, envoyAdminClusterName).
+ Configure(envoy_clusters.ProvidedEndpointCluster(
+ govalidator.IsIPv6(adminAddress),
+ core_xds.Endpoint{Target: adminAddress, Port: adminPort})).
+ Configure(envoy_clusters.DefaultTimeout()).
+ Build()
+ if err != nil {
+ return nil, err
+ }
+
+ resources := core_xds.NewResourceSet()
+
+ for _, se := range staticEndpointPaths {
+ se.ClusterName = envoyAdminClusterName
+ }
+
+ // We bind admin to 127.0.0.1 by default, creating another listener with same address and port will result in error.
+ if g.getAddress(proxy) != adminAddress {
+ filterChains := []envoy_listeners.ListenerBuilderOpt{
+ envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource).
+ Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticEndpointPaths)),
+ ),
+ }
+ for _, se := range staticTlsEndpointPaths {
+ se.ClusterName = envoyAdminClusterName
+ }
+ filterChains = append(filterChains, envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource).
+ Configure(envoy_listeners.MatchTransportProtocol("tls")).
+ Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticTlsEndpointPaths)).
+ Configure(envoy_listeners.ServerSideStaticMTLS(proxy.EnvoyAdminMTLSCerts)),
+ ))
+
+ listener, err := envoy_listeners.NewInboundListenerBuilder(proxy.APIVersion, g.getAddress(proxy), adminPort, core_xds.SocketAddressProtocolTCP).
+ WithOverwriteName(envoy_names.GetAdminListenerName()).
+ Configure(envoy_listeners.TLSInspector()).
+ Configure(filterChains...).
+ Build()
+ if err != nil {
+ return nil, err
+ }
+ resources.Add(&core_xds.Resource{
+ Name: listener.GetName(),
+ Origin: OriginAdmin,
+ Resource: listener,
+ })
+ }
+
+ resources.Add(&core_xds.Resource{
+ Name: cluster.GetName(),
+ Origin: OriginAdmin,
+ Resource: cluster,
+ })
+ return resources, nil
+}
+
+func (g AdminProxyGenerator) getAddress(proxy *core_xds.Proxy) string {
+ if proxy.Dataplane != nil {
+ return proxy.Dataplane.Spec.GetNetworking().Address
+ }
+
+ //TODO: ZoneEgressProxy
+ //if proxy.ZoneEgressProxy != nil {
+ // return proxy.ZoneEgressProxy.ZoneEgressResource.Spec.GetNetworking().GetAddress()
+ //}
+
+ if proxy.ZoneIngressProxy != nil {
+ return proxy.ZoneIngressProxy.ZoneIngressResource.Spec.GetNetworking().GetAddress()
+ }
+
+ return ""
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/generator/generator_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/generator/generator_suite_test.go
index 987f638..0c9c068 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/generator/generator_suite_test.go
@@ -15,25 +15,14 @@
* limitations under the License.
*/
-package dubbo
+package generator_test
import (
- "archive/zip"
- "bytes"
+ "testing"
+
+ "github.com/apache/dubbo-kubernetes/pkg/test"
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
- }
- return filesystem.NewZipFS(archive)
+func TestGenerator(t *testing.T) {
+ test.RunSpecs(t, "Generator Suite")
}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/generator/inbound_proxy_generator.go b/pkg/xds/generator/inbound_proxy_generator.go
index e811f5f..f8b5938 100644
--- a/pkg/xds/generator/inbound_proxy_generator.go
+++ b/pkg/xds/generator/inbound_proxy_generator.go
@@ -19,22 +19,28 @@
import (
"context"
-)
-import (
"github.com/pkg/errors"
-)
-import (
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
"github.com/apache/dubbo-kubernetes/pkg/util/net"
+
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+
envoy_clusters "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters"
+
envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+
envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+
+ "github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
)
const OriginInbound = "inbound"
@@ -92,7 +98,7 @@
// generate LDS resource
service := iface.GetService()
inboundListenerName := envoy_names.GetInboundListenerName(endpoint.DataplaneIP, endpoint.DataplanePort)
- filterChainBuilder := func(serverSideMTLS bool) *envoy_listeners.FilterChainBuilder {
+ filterChainBuilder := func(serverSideMTLS bool, ciphers tls.TlsCiphers, tlsVersion *tls.Version, xdsCtx xds_context.Context, endpoint mesh_proto.InboundInterface, service string, protocol core_mesh.Protocol, proxy *core_xds.Proxy) *envoy_listeners.FilterChainBuilder {
filterChainBuilder := envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource)
switch protocol {
case core_mesh.ProtocolTriple:
@@ -121,13 +127,23 @@
// configuration for non-HTTP cases
filterChainBuilder.Configure(envoy_listeners.TcpProxyDeprecated(localClusterName, envoy_common.NewCluster(envoy_common.WithService(localClusterName))))
}
+ if serverSideMTLS {
+ filterChainBuilder.Configure(envoy_listeners.ServerSideMTLS(xdsCtx.Mesh.Resource, proxy.SecretsTracker, tlsVersion, ciphers))
+ }
return filterChainBuilder
}
listenerBuilder := envoy_listeners.NewInboundListenerBuilder(proxy.APIVersion, endpoint.DataplaneIP, endpoint.DataplanePort, core_xds.SocketAddressProtocolTCP).
Configure(envoy_listeners.TagsMetadata(iface.GetTags()))
- listenerBuilder.Configure(envoy_listeners.FilterChain(filterChainBuilder(false)))
+ switch xdsCtx.Mesh.Resource.GetEnabledCertificateAuthorityBackend().GetMode() {
+ case mesh_proto.CertificateAuthorityBackend_STRICT:
+ //TODO: implement the logic of STRICT
+ case mesh_proto.CertificateAuthorityBackend_PERMISSIVE:
+ //TODO: implement the logic of PERMISSIVE
+ }
+
+ listenerBuilder.Configure(envoy_listeners.FilterChain(filterChainBuilder(false, nil, nil, xdsCtx, endpoint, service, protocol, proxy)))
inboundListener, err := listenerBuilder.Build()
if err != nil {
diff --git a/pkg/xds/generator/outbound_proxy_generator.go b/pkg/xds/generator/outbound_proxy_generator.go
index c01e8bb..849b3b5 100644
--- a/pkg/xds/generator/outbound_proxy_generator.go
+++ b/pkg/xds/generator/outbound_proxy_generator.go
@@ -20,13 +20,9 @@
import (
"context"
"fmt"
-)
-import (
"github.com/pkg/errors"
-)
-import (
mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
"github.com/apache/dubbo-kubernetes/pkg/core"
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
@@ -57,7 +53,7 @@
}
// TODO: implement the logic of tlsReadiness
- tlsReadiness := make(map[string]bool)
+ tlsReadiness := xdsCtx.Mesh.GetTlsReadiness()
servicesAcc := envoy_common.NewServicesAccumulator(tlsReadiness)
outboundsMultipleIPs := buildOutboundsWithMultipleIPs(proxy.Dataplane, outbounds)
@@ -156,6 +152,7 @@
for _, serviceName := range services.Sorted() {
service := services[serviceName]
protocol := ctx.Mesh.GetServiceProtocol(serviceName)
+ tlsReady := service.TLSReady()
for _, c := range service.Clusters() {
cluster := c.(*envoy_common.ClusterImpl)
@@ -163,17 +160,20 @@
edsClusterBuilder := envoy_clusters.NewClusterBuilder(proxy.APIVersion, clusterName)
// clusterTags := []envoy_tags.Tags{cluster.Tags()}
+ clusterTags := []envoy_tags.Tags{cluster.Tags()}
if service.HasExternalService() {
if ctx.Mesh.Resource.ZoneEgressEnabled() {
edsClusterBuilder.
- Configure(envoy_clusters.EdsCluster())
+ Configure(envoy_clusters.EdsCluster()).
+ Configure(envoy_clusters.ClientSideMTLS(proxy.SecretsTracker, ctx.Mesh.Resource, mesh_proto.ZoneEgressServiceName, tlsReady, clusterTags))
} else {
endpoints := proxy.Routing.ExternalServiceOutboundTargets[serviceName]
isIPv6 := proxy.Dataplane.IsIPv6()
edsClusterBuilder.
- Configure(envoy_clusters.ProvidedEndpointCluster(isIPv6, endpoints...))
+ Configure(envoy_clusters.ProvidedEndpointCluster(isIPv6, endpoints...)).
+ Configure(envoy_clusters.ClientSideTLS(endpoints))
}
switch protocol {
@@ -187,6 +187,22 @@
edsClusterBuilder.
Configure(envoy_clusters.EdsCluster()).
Configure(envoy_clusters.Http2())
+ if upstreamMeshName := cluster.Mesh(); upstreamMeshName != "" {
+ for _, otherMesh := range ctx.Mesh.Resources.Meshes().Items {
+ if otherMesh.GetMeta().GetName() == upstreamMeshName {
+ edsClusterBuilder.Configure(envoy_clusters.CrossMeshClientSideMTLS(proxy.SecretsTracker, ctx.Mesh.Resource, otherMesh, serviceName, tlsReady, clusterTags))
+ break
+ }
+ }
+ } else {
+ edsClusterBuilder.Configure(envoy_clusters.ClientSideMTLS(
+ proxy.SecretsTracker,
+ ctx.Mesh.Resource,
+ serviceName,
+ tlsReady,
+ clusterTags,
+ ))
+ }
}
edsCluster, err := edsClusterBuilder.Build()
diff --git a/pkg/xds/generator/proxy_template.go b/pkg/xds/generator/proxy_template.go
index 6bcebc7..1073dda 100644
--- a/pkg/xds/generator/proxy_template.go
+++ b/pkg/xds/generator/proxy_template.go
@@ -20,11 +20,10 @@
import (
"context"
"fmt"
-)
-import (
model "github.com/apache/dubbo-kubernetes/pkg/core/xds"
"github.com/apache/dubbo-kubernetes/pkg/plugins/policies/core/generator"
+
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
"github.com/apache/dubbo-kubernetes/pkg/xds/generator/core"
)
@@ -58,6 +57,7 @@
func NewDefaultProxyProfile() core.ResourceGenerator {
return core.CompositeResourceGenerator{
+ AdminProxyGenerator{},
InboundProxyGenerator{},
OutboundProxyGenerator{},
generator.NewGenerator(),
diff --git a/pkg/xds/generator/secrets/generator.go b/pkg/xds/generator/secrets/generator.go
new file mode 100644
index 0000000..f028863
--- /dev/null
+++ b/pkg/xds/generator/secrets/generator.go
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package secrets
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+ xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+ envoy_secrets "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/secrets/v3"
+ generator_core "github.com/apache/dubbo-kubernetes/pkg/xds/generator/core"
+)
+
+// OriginSecrets is a marker to indicate by which ProxyGenerator resources were generated.
+const OriginSecrets = "secrets"
+
+type Generator struct{}
+
+var _ generator_core.ResourceGenerator = Generator{}
+
+func createCaSecretResource(name string, ca *core_xds.CaSecret) *core_xds.Resource {
+ caSecret := envoy_secrets.CreateCaSecret(ca, name)
+ return &core_xds.Resource{
+ Name: caSecret.Name,
+ Origin: OriginSecrets,
+ Resource: caSecret,
+ }
+}
+
+func createIdentitySecretResource(name string, identity *core_xds.IdentitySecret) *core_xds.Resource {
+ identitySecret := envoy_secrets.CreateIdentitySecret(identity, name)
+ return &core_xds.Resource{
+ Name: identitySecret.Name,
+ Origin: OriginSecrets,
+ Resource: identitySecret,
+ }
+}
+
+// GenerateForZoneEgress generates whatever secrets were referenced in the
+// zone egress config generation.
+func (g Generator) GenerateForZoneEgress(
+ ctx context.Context,
+ xdsCtx xds_context.Context,
+ proxyId core_xds.ProxyId,
+ zoneEgressResource *core_mesh.ZoneEgressResource,
+ secretsTracker core_xds.SecretsTracker,
+ mesh *core_mesh.MeshResource,
+) (*core_xds.ResourceSet, error) {
+ resources := core_xds.NewResourceSet()
+
+ log := core.Log.WithName("secrets-generator").WithValues("proxyID", proxyId.String())
+
+ if !mesh.MTLSEnabled() {
+ return nil, nil
+ }
+
+ meshName := mesh.GetMeta().GetName()
+
+ usedIdentity := secretsTracker.UsedIdentity()
+
+ identity, ca, err := xdsCtx.ControlPlane.Secrets.GetForZoneEgress(ctx, zoneEgressResource, mesh)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate ZoneEgress secrets")
+ }
+
+ if usedIdentity {
+ log.V(1).Info("added identity", "mesh", meshName)
+ resources.Add(createIdentitySecretResource(secretsTracker.RequestIdentityCert().Name(), identity))
+ }
+
+ if _, ok := secretsTracker.UsedCas()[meshName]; ok {
+ log.V(1).Info("added mesh CA resources", "mesh", meshName)
+ resources.Add(createCaSecretResource(secretsTracker.RequestCa(meshName).Name(), ca))
+ }
+
+ return resources, nil
+}
+
+// Generate uses the SecretsTracker on Proxy and
+// generates whatever secrets were used in the config generation.
+func (g Generator) Generator(
+ ctx context.Context,
+ _ *core_xds.ResourceSet,
+ xdsCtx xds_context.Context,
+ proxy *core_xds.Proxy,
+) (*core_xds.ResourceSet, error) {
+ resources := core_xds.NewResourceSet()
+
+ log := core.Log.WithName("secrets-generator").WithValues("proxyID", proxy.Id.String())
+
+ if proxy.Dataplane != nil {
+ log = log.WithValues("mesh", xdsCtx.Mesh.Resource.GetMeta().GetName())
+ }
+
+ usedIdentity := proxy.SecretsTracker.UsedIdentity()
+ usedCas := proxy.SecretsTracker.UsedCas()
+ usedAllInOne := proxy.SecretsTracker.UsedAllInOne()
+
+ if usedAllInOne {
+ otherMeshes := xdsCtx.Mesh.Resources.OtherMeshes().Items
+ identity, allInOneCa, err := xdsCtx.ControlPlane.Secrets.GetAllInOne(ctx, xdsCtx.Mesh.Resource, proxy.Dataplane, otherMeshes)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate all in one CA")
+ }
+
+ resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestAllInOneCa().Name(), allInOneCa))
+ resources.Add(createIdentitySecretResource(proxy.SecretsTracker.RequestIdentityCert().Name(), identity))
+ log.V(1).Info("added all in one CA resources")
+ }
+
+ if usedIdentity || len(usedCas) > 0 {
+ var otherMeshes []*core_mesh.MeshResource
+ for _, otherMesh := range xdsCtx.Mesh.Resources.OtherMeshes().Items {
+ if _, ok := usedCas[otherMesh.GetMeta().GetName()]; ok {
+ otherMeshes = append(otherMeshes, otherMesh)
+ }
+ }
+ identity, generatedMeshCAs, err := xdsCtx.ControlPlane.Secrets.GetForDataPlane(ctx, proxy.Dataplane, xdsCtx.Mesh.Resource, otherMeshes)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate dataplane identity cert and CAs")
+ }
+
+ resources.Add(createIdentitySecretResource(proxy.SecretsTracker.RequestIdentityCert().Name(), identity))
+
+ var addedCas []string
+ for mesh := range usedCas {
+ if ca, ok := generatedMeshCAs[mesh]; ok {
+ resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestCa(mesh).Name(), ca))
+ } else {
+ // We need to add _something_ here so that Envoy syncs the
+ // config
+ emptyCa := &core_xds.CaSecret{}
+ resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestCa(mesh).Name(), emptyCa))
+ }
+ addedCas = append(addedCas, mesh)
+ }
+ log.V(1).Info("added identity and mesh CAs resources", "cas", addedCas)
+ }
+
+ return resources, nil
+}
diff --git a/pkg/xds/generator/zoneproxy/generator.go b/pkg/xds/generator/zoneproxy/generator.go
index 1187456..dd9d8f3 100644
--- a/pkg/xds/generator/zoneproxy/generator.go
+++ b/pkg/xds/generator/zoneproxy/generator.go
@@ -26,6 +26,7 @@
envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
)
func GenerateCDS(
@@ -109,6 +110,8 @@
) envoy_common.Services {
servicesAcc := envoy_common.NewServicesAccumulator(nil)
+ sniUsed := map[string]struct{}{}
+
for _, service := range availableServices {
serviceName := service.Tags[mesh_proto.ServiceTag]
destinations := destinationsPerService[serviceName]
@@ -117,6 +120,14 @@
serviceEndpoints := endpointMap[serviceName]
for _, destination := range destinations {
+ sni := tls.SNIFromTags(destination.
+ WithTags(mesh_proto.ServiceTag, serviceName).
+ WithTags("mesh", service.Mesh),
+ )
+ if _, ok := sniUsed[sni]; ok {
+ continue
+ }
+ sniUsed[sni] = struct{}{}
// relevantTags is a set of tags for which it actually makes sense to do LB split on.
// If the endpoint list is the same with or without the tag, we should just not do the split.
@@ -155,6 +166,8 @@
filterChain := envoy_listeners.FilterChain(
envoy_listeners.NewFilterChainBuilder(apiVersion, envoy_common.AnonymousResource).Configure(
+ envoy_listeners.MatchTransportProtocol("tls"),
+ envoy_listeners.MatchServerNames(sni),
envoy_listeners.TcpProxyDeprecatedWithMetadata(
clusterName,
cluster,
diff --git a/pkg/xds/secrets/ca_provider.go b/pkg/xds/secrets/ca_provider.go
new file mode 100644
index 0000000..1d6c68f
--- /dev/null
+++ b/pkg/xds/secrets/ca_provider.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package secrets
+
+import (
+ "context"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/prometheus/client_golang/prometheus"
+
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+type CaProvider interface {
+ // Get returns all PEM encoded CAs, a list of CAs that were used to generate a secret and an error.
+ Get(context.Context, *core_mesh.MeshResource) (*core_xds.CaSecret, []string, error)
+}
+
+func NewCaProvider(caManagers core_ca.Managers) (CaProvider, error) {
+ return &meshCaProvider{
+ caManagers: caManagers,
+ }, nil
+}
+
+type meshCaProvider struct {
+ caManagers core_ca.Managers
+ latencyMetrics *prometheus.SummaryVec
+}
+
+// Get retrieves the root CA for a given backend with a default timeout of 10
+// seconds.
+func (s *meshCaProvider) Get(ctx context.Context, mesh *core_mesh.MeshResource) (*core_xds.CaSecret, []string, error) {
+ backend := mesh.GetEnabledCertificateAuthorityBackend()
+ if backend == nil {
+ return nil, nil, errors.New("CA backend is nil")
+ }
+
+ //TODO:对用户自定义超时时间支持
+ timeout := 10 * time.Second
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ caManager, exist := s.caManagers[backend.Type]
+ if !exist {
+ return nil, nil, errors.Errorf("CA manager of type %s not exist", backend.Type)
+ }
+
+ var certs [][]byte
+ var err error
+ func() {
+ start := time.Now()
+ defer func() {
+ s.latencyMetrics.WithLabelValues(backend.GetName()).Observe(float64(time.Since(start).Milliseconds()))
+ }()
+ certs, err = caManager.GetRootCert(ctx, mesh.GetMeta().GetName(), backend)
+ }()
+ if err != nil {
+ return nil, nil, errors.Wrap(err, "could not get root certs")
+ }
+
+ return &core_xds.CaSecret{
+ PemCerts: certs,
+ }, []string{backend.Name}, nil
+}
diff --git a/pkg/xds/secrets/identity_provider.go b/pkg/xds/secrets/identity_provider.go
new file mode 100644
index 0000000..522fbc6
--- /dev/null
+++ b/pkg/xds/secrets/identity_provider.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package secrets
+
+import (
+ "context"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/prometheus/client_golang/prometheus"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+type Identity struct {
+ Mesh string
+ Name string
+ Services mesh_proto.MultiValueTagSet
+}
+
+type IdentityProvider interface {
+ // Get returns PEM encoded cert + key, backend that was used to generate this pair and an error.
+ Get(context.Context, Identity, *core_mesh.MeshResource) (*core_xds.IdentitySecret, string, error)
+}
+
+type identityCertProvider struct {
+ caManagers core_ca.Managers
+ latencyMetrics *prometheus.SummaryVec
+}
+
+func NewIdentityProvider(caManagers core_ca.Managers) (IdentityProvider, error) {
+ return &identityCertProvider{
+ caManagers: caManagers,
+ }, nil
+}
+
+func (s *identityCertProvider) Get(ctx context.Context, requestor Identity, mesh *core_mesh.MeshResource) (*core_xds.IdentitySecret, string, error) {
+ backend := mesh.GetEnabledCertificateAuthorityBackend()
+ if backend == nil {
+ return nil, "", errors.Errorf("CA default backend in mesh %q has to be defined", mesh.GetMeta().GetName())
+ }
+
+ timeout := backend.GetDpCert().GetRequestTimeout()
+ if timeout != nil {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, timeout.AsDuration())
+ defer cancel()
+ }
+
+ caManager, exist := s.caManagers[backend.Type]
+ if !exist {
+ return nil, "", errors.Errorf("CA manager of type %s not exist", backend.Type)
+ }
+
+ var pair core_ca.KeyPair
+ var err error
+ func() {
+ start := time.Now()
+ defer func() {
+ s.latencyMetrics.WithLabelValues(backend.GetName()).Observe(float64(time.Since(start).Milliseconds()))
+ }()
+ pair, err = caManager.GenerateDataplaneCert(ctx, mesh.GetMeta().GetName(), backend, requestor.Services)
+ }()
+
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "could not generate dataplane cert for mesh: %q backend: %q services: %q", mesh.GetMeta().GetName(), backend.Name, requestor.Services)
+ }
+
+ return &core_xds.IdentitySecret{
+ PemCerts: [][]byte{pair.CertPEM},
+ PemKey: pair.KeyPEM,
+ }, backend.Name, nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/secrets/kind.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/secrets/kind.go
index 987f638..70342e9 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/secrets/kind.go
@@ -15,25 +15,31 @@
* limitations under the License.
*/
-package dubbo
+package secrets
-import (
- "archive/zip"
- "bytes"
+type ChangeKind int
+
+const (
+ IdentityChange ChangeKind = iota
+ OwnMeshChange
+ OtherMeshChange
)
-import (
- "github.com/apache/dubbo-kubernetes/dubboctl/generated"
- "github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type UpdateKinds map[ChangeKind]struct{}
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
- archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
- if err != nil {
- panic(err)
+func UpdateEverything() UpdateKinds {
+ return map[ChangeKind]struct{}{
+ IdentityChange: {},
+ OwnMeshChange: {},
+ OtherMeshChange: {},
}
- return filesystem.NewZipFS(archive)
}
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (kinds UpdateKinds) HasType(kind ChangeKind) bool {
+ _, ok := kinds[kind]
+ return ok
+}
+
+func (kinds UpdateKinds) AddKind(kind ChangeKind) {
+ kinds[kind] = struct{}{}
+}
diff --git a/pkg/xds/secrets/secrets.go b/pkg/xds/secrets/secrets.go
new file mode 100644
index 0000000..df15766
--- /dev/null
+++ b/pkg/xds/secrets/secrets.go
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package secrets
+
+import (
+ "context"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/prometheus/client_golang/prometheus"
+ "google.golang.org/protobuf/proto"
+
+ mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-kubernetes/pkg/core"
+ core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+ "github.com/apache/dubbo-kubernetes/pkg/core/user"
+ core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+var log = core.Log.WithName("xds").WithName("secrets")
+
+type MeshCa struct {
+ Mesh string
+ CaSecret *core_xds.CaSecret
+}
+
+type Secrets interface {
+ GetForDataPlane(ctx context.Context, dataplane *core_mesh.DataplaneResource, mesh *core_mesh.MeshResource, otherMeshes []*core_mesh.MeshResource) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, error)
+ GetForZoneEgress(ctx context.Context, zoneEgress *core_mesh.ZoneEgressResource, mesh *core_mesh.MeshResource) (*core_xds.IdentitySecret, *core_xds.CaSecret, error)
+ GetAllInOne(ctx context.Context, mesh *core_mesh.MeshResource, dataplane *core_mesh.DataplaneResource, otherMeshes []*core_mesh.MeshResource) (*core_xds.IdentitySecret, *core_xds.CaSecret, error)
+ Info(mesh_proto.ProxyType, model.ResourceKey) *Info
+ Cleanup(mesh_proto.ProxyType, model.ResourceKey)
+}
+
+type MeshInfo struct {
+ MTLS *mesh_proto.Mesh_Mtls
+}
+
+type Info struct {
+ Expiration time.Time
+ Generation time.Time
+
+ Tags mesh_proto.MultiValueTagSet
+
+ IssuedBackend string
+ SupportedBackends []string
+
+ OwnMesh MeshInfo
+ OtherMeshInfos []MeshInfo
+ // this marks our info as having failed last time to get the mesh CAs that
+ // we wanted and so we should retry next time we want certs.
+ failedOtherMeshes bool
+}
+
+func (c *Info) CertLifetime() time.Duration {
+ return c.Expiration.Sub(c.Generation)
+}
+
+func (c *Info) ExpiringSoon() bool {
+ return core.Now().After(c.Generation.Add(c.CertLifetime() / 5 * 4))
+}
+
+func NewSecrets(caProvider CaProvider, identityProvider IdentityProvider) (Secrets, error) {
+
+ return &secrets{
+ caProvider: caProvider,
+ identityProvider: identityProvider,
+ cachedCerts: map[certCacheKey]*certs{},
+ }, nil
+}
+
+type certCacheKey struct {
+ resource model.ResourceKey
+ proxyType mesh_proto.ProxyType
+}
+
+type secrets struct {
+ caProvider CaProvider
+ identityProvider IdentityProvider
+
+ sync.RWMutex
+ cachedCerts map[certCacheKey]*certs
+ certGenerationsMetric *prometheus.CounterVec
+}
+
+var _ Secrets = &secrets{}
+
+func (s *secrets) Info(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) *Info {
+ certs := s.certs(proxyType, dpKey)
+ if certs == nil {
+ return nil
+ }
+ return certs.info
+}
+
+type certs struct {
+ identity *core_xds.IdentitySecret
+ ownCa MeshCa
+ otherCas []MeshCa
+ allInOneCa MeshCa
+ info *Info
+}
+
+func (c *certs) Info() *Info {
+ if c == nil {
+ return nil
+ }
+ return c.info
+}
+
+func (s *secrets) certs(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) *certs {
+ s.RLock()
+ defer s.RUnlock()
+
+ return s.cachedCerts[certCacheKey{proxyType: proxyType, resource: dpKey}]
+}
+
+func (s *secrets) GetForDataPlane(
+ ctx context.Context,
+ dataplane *core_mesh.DataplaneResource,
+ mesh *core_mesh.MeshResource,
+ otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, error) {
+ identity, cas, _, err := s.get(ctx, mesh_proto.DataplaneProxyType, dataplane, dataplane.Spec.TagSet(), mesh, otherMeshes)
+ return identity, cas, err
+}
+
+func (s *secrets) GetAllInOne(
+ ctx context.Context,
+ mesh *core_mesh.MeshResource,
+ dataplane *core_mesh.DataplaneResource,
+ otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, *core_xds.CaSecret, error) {
+ identity, _, allInOne, err := s.get(ctx, mesh_proto.DataplaneProxyType, dataplane, dataplane.Spec.TagSet(), mesh, otherMeshes)
+ return identity, allInOne.CaSecret, err
+}
+
+func (s *secrets) GetForZoneEgress(
+ ctx context.Context,
+ zoneEgress *core_mesh.ZoneEgressResource,
+ mesh *core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, *core_xds.CaSecret, error) {
+ tags := mesh_proto.MultiValueTagSetFrom(map[string][]string{
+ mesh_proto.ServiceTag: {
+ mesh_proto.ZoneEgressServiceName,
+ },
+ })
+
+ identity, cas, _, err := s.get(ctx, mesh_proto.EgressProxyType, zoneEgress, tags, mesh, nil)
+ return identity, cas[mesh.GetMeta().GetName()], err
+}
+
+func (s *secrets) get(
+ ctx context.Context,
+ proxyType mesh_proto.ProxyType,
+ resource model.Resource,
+ tags mesh_proto.MultiValueTagSet,
+ mesh *core_mesh.MeshResource,
+ otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, MeshCa, error) {
+ if !mesh.MTLSEnabled() {
+ return nil, nil, MeshCa{}, nil
+ }
+
+ meshName := mesh.GetMeta().GetName()
+
+ resourceKey := model.MetaToResourceKey(resource.GetMeta())
+ resourceKey.Mesh = meshName
+ certs := s.certs(proxyType, resourceKey)
+
+ if updateKinds, debugReason := s.shouldGenerateCerts(
+ certs.Info(),
+ tags,
+ mesh,
+ otherMeshes,
+ ); len(updateKinds) > 0 {
+ log.Info(
+ "generating certificate",
+ string(resource.Descriptor().Name), resourceKey, "reason", debugReason,
+ )
+
+ certs, err := s.generateCerts(ctx, tags, mesh, otherMeshes, certs, updateKinds)
+ if err != nil {
+ return nil, nil, MeshCa{}, errors.Wrap(err, "could not generate certificates")
+ }
+
+ key := certCacheKey{
+ resource: resourceKey,
+ proxyType: proxyType,
+ }
+ s.Lock()
+ s.cachedCerts[key] = certs
+ s.Unlock()
+
+ caMap := map[string]*core_xds.CaSecret{
+ meshName: certs.ownCa.CaSecret,
+ }
+ for _, otherCa := range certs.otherCas {
+ caMap[otherCa.Mesh] = otherCa.CaSecret
+ }
+ return certs.identity, caMap, certs.allInOneCa, nil
+ }
+
+ if certs == nil { // previous "if" should guarantee that the certs are always there
+ return nil, nil, MeshCa{}, errors.New("certificates were not generated")
+ }
+
+ caMap := map[string]*core_xds.CaSecret{
+ meshName: certs.ownCa.CaSecret,
+ }
+ for _, otherCa := range certs.otherCas {
+ caMap[otherCa.Mesh] = otherCa.CaSecret
+ }
+
+ return certs.identity, caMap, certs.allInOneCa, nil
+}
+
+func (s *secrets) Cleanup(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) {
+ key := certCacheKey{
+ resource: dpKey,
+ proxyType: proxyType,
+ }
+ s.Lock()
+ delete(s.cachedCerts, key)
+ s.Unlock()
+}
+
+func (s *secrets) shouldGenerateCerts(info *Info, tags mesh_proto.MultiValueTagSet, ownMesh *core_mesh.MeshResource, otherMeshInfos []*core_mesh.MeshResource) (UpdateKinds, string) {
+ if info == nil {
+ return UpdateEverything(), "mTLS is enabled and DP hasn't received a certificate yet"
+ }
+
+ var reason string
+ updates := UpdateKinds{}
+
+ if !proto.Equal(info.OwnMesh.MTLS, ownMesh.Spec.Mtls) {
+ updates.AddKind(OwnMeshChange)
+ reason = "Mesh mTLS settings have changed"
+ }
+
+ if len(info.OtherMeshInfos) != len(otherMeshInfos) || info.failedOtherMeshes {
+ updates.AddKind(OtherMeshChange)
+ reason = "Another mesh has been added or removed or we must retry"
+ } else {
+ for i, mesh := range info.OtherMeshInfos {
+ if !proto.Equal(mesh.MTLS, otherMeshInfos[i].Spec.Mtls) {
+ updates.AddKind(OtherMeshChange)
+ reason = "Another Mesh's mTLS settings have changed"
+ break
+ }
+ }
+ }
+
+ if tags.String() != info.Tags.String() {
+ updates.AddKind(IdentityChange)
+ reason = "DP tags have changed"
+ }
+
+ if info.ExpiringSoon() {
+ updates.AddKind(IdentityChange)
+ reason = fmt.Sprintf("the certificate expiring soon. Generated at %q, expiring at %q", info.Generation, info.Expiration)
+ }
+
+ return updates, reason
+}
+
+func (s *secrets) generateCerts(
+ ctx context.Context,
+ tags mesh_proto.MultiValueTagSet,
+ mesh *core_mesh.MeshResource,
+ otherMeshes []*core_mesh.MeshResource,
+ oldCerts *certs,
+ updateKinds UpdateKinds,
+) (*certs, error) {
+ ctx = user.Ctx(ctx, user.ControlPlane)
+ var identity *core_xds.IdentitySecret
+ var ownCa MeshCa
+ var otherCas []MeshCa
+ var allInOneCa MeshCa
+ info := &Info{}
+
+ if oldCerts != nil {
+ identity = oldCerts.identity
+ ownCa = oldCerts.ownCa
+ otherCas = oldCerts.otherCas
+ allInOneCa = oldCerts.allInOneCa
+ info = oldCerts.Info()
+ }
+
+ meshName := mesh.GetMeta().GetName()
+
+ if updateKinds.HasType(IdentityChange) || updateKinds.HasType(OwnMeshChange) {
+ requester := Identity{
+ Services: tags,
+ Mesh: meshName,
+ }
+
+ identitySecret, issuedBackend, err := s.identityProvider.Get(ctx, requester, mesh)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not get Dataplane cert pair")
+ }
+
+ s.certGenerationsMetric.WithLabelValues(requester.Mesh).Inc()
+
+ block, _ := pem.Decode(identitySecret.PemCerts[0])
+ cert, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not extract info about certificate")
+ }
+
+ info.Tags = tags
+ info.IssuedBackend = issuedBackend
+ info.Expiration = cert.NotAfter
+ info.Generation = core.Now()
+ identity = identitySecret
+ }
+
+ if updateKinds.HasType(OwnMeshChange) {
+ caSecret, supportedBackends, err := s.caProvider.Get(ctx, mesh)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not get mesh CA cert")
+ }
+
+ ownCa = MeshCa{
+ Mesh: meshName,
+ CaSecret: caSecret,
+ }
+ info.SupportedBackends = supportedBackends
+ info.OwnMesh = MeshInfo{
+ MTLS: mesh.Spec.Mtls,
+ }
+ }
+
+ if updateKinds.HasType(OtherMeshChange) || updateKinds.HasType(OwnMeshChange) {
+ var otherMeshInfos []MeshInfo
+ var bytes [][]byte
+ var names []string
+ otherCas = []MeshCa{}
+
+ failedOtherMeshes := false
+ for _, otherMesh := range otherMeshes {
+ otherMeshInfos = append(otherMeshInfos, MeshInfo{
+ MTLS: otherMesh.Spec.GetMtls(),
+ })
+
+ // We need to track this mesh but we don't do anything with certs
+ if !otherMesh.MTLSEnabled() {
+ continue
+ }
+
+ otherCa, _, err := s.caProvider.Get(ctx, otherMesh)
+ if err != nil {
+ failedOtherMeshes = true
+ // The other CA is misconfigured but this can not affect
+ // generation in this mesh.
+ log.Error(err, "could not get other mesh CA cert")
+ continue
+ }
+
+ meshName := otherMesh.GetMeta().GetName()
+
+ otherCas = append(otherCas, MeshCa{
+ Mesh: meshName,
+ CaSecret: otherCa,
+ })
+
+ names = append(names, meshName)
+ bytes = append(bytes, otherCa.PemCerts...)
+ }
+
+ names = append(names, meshName)
+ bytes = append(bytes, ownCa.CaSecret.PemCerts...)
+
+ sort.Strings(names)
+ allInOneCa = MeshCa{
+ Mesh: strings.Join(names, ":"),
+ CaSecret: &core_xds.CaSecret{
+ PemCerts: bytes,
+ },
+ }
+
+ info.failedOtherMeshes = failedOtherMeshes
+ info.OtherMeshInfos = otherMeshInfos
+ }
+
+ return &certs{
+ identity: identity,
+ ownCa: ownCa,
+ otherCas: otherCas,
+ allInOneCa: allInOneCa,
+ info: info,
+ }, nil
+}
diff --git a/pkg/xds/server/components.go b/pkg/xds/server/components.go
index 87926f5..ed5a22f 100644
--- a/pkg/xds/server/components.go
+++ b/pkg/xds/server/components.go
@@ -18,17 +18,21 @@
package server
import (
+ "github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
"github.com/pkg/errors"
-)
-import (
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+
core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+
core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
+
util_xds "github.com/apache/dubbo-kubernetes/pkg/util/xds"
"github.com/apache/dubbo-kubernetes/pkg/xds/cache/cla"
+
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
v3 "github.com/apache/dubbo-kubernetes/pkg/xds/server/v3"
)
@@ -49,9 +53,20 @@
return err
}
+ idProvider, err := secrets.NewIdentityProvider(rt.CaManagers())
+ if err != nil {
+ return err
+ }
+
+ secrets, err := secrets.NewSecrets(
+ rt.CAProvider(),
+ idProvider,
+ )
+
envoyCpCtx := &xds_context.ControlPlaneContext{
CLACache: claCache,
Zone: "",
+ Secrets: secrets,
}
if err := v3.RegisterXDS(statsCallbacks, envoyCpCtx, rt); err != nil {
return errors.Wrap(err, "could not register V3 XDS")
diff --git a/pkg/xds/sync/dataplane_watchdog.go b/pkg/xds/sync/dataplane_watchdog.go
index 72d239f..9604ac5 100644
--- a/pkg/xds/sync/dataplane_watchdog.go
+++ b/pkg/xds/sync/dataplane_watchdog.go
@@ -19,6 +19,8 @@
import (
"context"
+ envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+ util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
)
import (
@@ -71,6 +73,7 @@
dpType mesh_proto.ProxyType
proxyTypeSettled bool
dpAddress string
+ envoyAdminMTLS *core_xds.ServerSideMTLSCerts
}
func NewDataplaneWatchdog(deps DataplaneWatchdogDependencies, dpKey core_model.ResourceKey) *DataplaneWatchdog {
@@ -130,7 +133,15 @@
ProxyType: mesh_proto.IngressProxyType,
}
syncForConfig := aggregatedMeshCtxs.Hash != d.lastHash
- if !syncForConfig {
+ var syncForCert bool
+ for _, mesh := range aggregatedMeshCtxs.Meshes {
+ certInfo := d.EnvoyCpCtx.Secrets.Info(
+ mesh_proto.IngressProxyType,
+ core_model.ResourceKey{Mesh: mesh.GetMeta().GetName(), Name: d.key.Name},
+ )
+ syncForCert = syncForCert || (certInfo != nil && certInfo.ExpiringSoon()) // check if we need to regenerate config because identity cert is expiring soon.
+ }
+ if !syncForConfig && !syncForCert {
result.Status = SkipStatus
return result, nil
}
@@ -138,10 +149,22 @@
d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", aggregatedMeshCtxs.Hash)
d.lastHash = aggregatedMeshCtxs.Hash
+ if syncForCert {
+ d.log.V(1).Info("certs expiring soon, reconcile")
+ }
+
proxy, err := d.IngressProxyBuilder.Build(ctx, d.key, aggregatedMeshCtxs)
if err != nil {
return SyncResult{}, errors.Wrap(err, "could not build ingress proxy")
}
+
+ networking := proxy.ZoneIngressProxy.ZoneIngressResource.Spec.GetNetworking()
+ envoyAdminMTLS, err := d.getEnvoyAdminMTLS(ctx, networking.GetAddress(), networking.GetAdvertisedAddress())
+ if err != nil {
+ return SyncResult{}, errors.Wrap(err, "could not get Envoy Admin mTLS certs")
+ }
+ proxy.EnvoyAdminMTLSCerts = envoyAdminMTLS
+
proxy.Metadata = metadata
changed, err := d.IngressReconciler.Reconcile(ctx, *envoyCtx, proxy)
if err != nil {
@@ -166,27 +189,48 @@
if err != nil {
return SyncResult{}, errors.Wrap(err, "could not get mesh context")
}
+
+ certInfo := d.EnvoyCpCtx.Secrets.Info(mesh_proto.DataplaneProxyType, d.key)
+ // check if we need to regenerate config because identity cert is expiring soon.
+ syncForCert := certInfo != nil && certInfo.ExpiringSoon()
// check if we need to regenerate config because Dubbo policies has changed.
syncForConfig := meshCtx.Hash != d.lastHash
result := SyncResult{
ProxyType: mesh_proto.DataplaneProxyType,
}
- if !syncForConfig {
+ if !syncForConfig && !syncForCert {
result.Status = SkipStatus
return result, nil
}
if syncForConfig {
d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", meshCtx.Hash)
}
+ if syncForCert {
+ d.log.V(1).Info("certs expiring soon, reconcile")
+ }
envoyCtx := &xds_context.Context{
ControlPlane: d.EnvoyCpCtx,
Mesh: meshCtx,
}
+
+ if _, found := meshCtx.DataplanesByName[d.key.Name]; !found {
+ d.log.Info("Dataplane object not found. Can't regenerate XDS configuration. It's expected during Kubernetes namespace termination. " +
+ "If it persists it's a bug.")
+ result.Status = SkipStatus
+ return result, nil
+ }
+
proxy, err := d.DataplaneProxyBuilder.Build(ctx, d.key, meshCtx)
if err != nil {
return SyncResult{}, errors.Wrap(err, "could not build dataplane proxy")
}
+ networking := proxy.Dataplane.Spec.Networking
+ envoyAdminMTLS, err := d.getEnvoyAdminMTLS(ctx, networking.Address, networking.AdvertisedAddress)
+ proxy.EnvoyAdminMTLSCerts = envoyAdminMTLS
+ if !envoyCtx.Mesh.Resource.MTLSEnabled() {
+ d.EnvoyCpCtx.Secrets.Cleanup(mesh_proto.DataplaneProxyType, d.key) // we need to cleanup secrets if mtls is disabled
+ }
proxy.Metadata = metadata
changed, err := d.DataplaneReconciler.Reconcile(ctx, *envoyCtx, proxy)
if err != nil {
@@ -201,3 +245,37 @@
}
return result, nil
}
+
+func (d *DataplaneWatchdog) getEnvoyAdminMTLS(ctx context.Context, address string, advertisedAddress string) (core_xds.ServerSideMTLSCerts, error) {
+ if d.envoyAdminMTLS == nil || d.dpAddress != address {
+ ca, err := envoy_admin_tls.LoadCA(ctx, d.ResManager)
+ if err != nil {
+ return core_xds.ServerSideMTLSCerts{}, errors.Wrap(err, "could not load the CA")
+ }
+ caPair, err := util_tls.ToKeyPair(ca.PrivateKey, ca.Certificate[0])
+ if err != nil {
+ return core_xds.ServerSideMTLSCerts{}, err
+ }
+ ips := []string{address}
+ if advertisedAddress != "" && advertisedAddress != address {
+ ips = append(ips, advertisedAddress)
+ }
+ serverPair, err := envoy_admin_tls.GenerateServerCert(ca, ips...)
+ if err != nil {
+ return core_xds.ServerSideMTLSCerts{}, errors.Wrap(err, "could not generate server certificate")
+ }
+
+ envoyAdminMTLS := core_xds.ServerSideMTLSCerts{
+ CaPEM: caPair.CertPEM,
+ ServerPair: serverPair,
+ }
+ // cache the Envoy Admin MTLS and dp address, so we
+ // 1) don't have to do I/O on every sync
+ // 2) have a stable certs = stable Envoy config
+ // This means that if we want to change Envoy Admin CA, we need to restart all CP instances.
+ // Additionally, we need to trigger cert generation when DP address has changed without DP reconnection.
+ d.envoyAdminMTLS = &envoyAdminMTLS
+ d.dpAddress = address
+ }
+ return *d.envoyAdminMTLS, nil
+}