diff --git a/docs/cookbook/README.md b/docs/cookbook/README.md new file mode 100644 index 00000000..8250d205 --- /dev/null +++ b/docs/cookbook/README.md @@ -0,0 +1,3 @@ +# Cookbook for Common User Cases + +- [External Service Registry](./external_service_registry.md) - Co-exist with external service registry. diff --git a/docs/cookbook/external_service_registry.md b/docs/cookbook/external_service_registry.md new file mode 100644 index 00000000..4abe185b --- /dev/null +++ b/docs/cookbook/external_service_registry.md @@ -0,0 +1,53 @@ + +# External Service Registry + +When architecture style moves to service mesh, there could be a middle status for the technical stack. Which is that there would be both legacy services and mesh services co-existing. The two different kinds of services also want to call each other. + +EaseMesh uses internal component Etcd in the control plane to play the role of service registry, and the external services may use Consul, Nacos, Eureka, Zookeeper as a service registry. Based on service registry controllers from Easegress, we develop a registry syncer to synchronize services between internal Etcd and the external service registry. + +As an example, we use `emctl` to apply consul service registry: + +```bash +$ emctl apply -f consul-service-registry.yaml +``` + +consul-service-registry.yaml + +```yaml +apiVersion: mesh.megaease.com/v1alpha1 +kind: ConsulServiceRegistry +metadata: + name: consul-service-registry +address: consul-server-0.consul-server.default:8500 +scheme: http +datacenter: "" +token: "" +namespace: "" +syncInterval: 5s +serviceTags: [] +``` + +Then we need to specify external service registry name in MeshController: + +```bash +$ emctl apply -f mesh-controller.yaml +``` + +mesh-controller.yaml + +```yaml +apiVersion: mesh.megaease.com/v1alpha1 +kind: MeshController +metadata: + name: easemesh-controller +apiPort: 13009 +ingressPort: 19527 +heartbeatInterval: 5s +# + +externalServiceRegistry: consul-service-registry +registryType: consul +``` + +And we could use `emctl get service && emctl get serviceinstance` to query the service and instance information from different sources. + +> NOTICE: The registry syncer transforms the service entry format between different registries. The connectability needs to be guaranteed by themselves. For example, services run in the same Kubernetes cluster would be connectable without other operations. diff --git a/emctl/cmd/client/command/apply/applier.go b/emctl/cmd/client/command/apply/applier.go index 424dcc30..be92e456 100644 --- a/emctl/cmd/client/command/apply/applier.go +++ b/emctl/cmd/client/command/apply/applier.go @@ -46,6 +46,8 @@ func WrapApplierByMeshObject(object meta.MeshObject, switch object.Kind() { case resource.KindMeshController: return &meshControllerApplier{object: object.(*resource.MeshController), baseApplier: baseApplier{client: client, timeout: timeout}} + case resource.KindConsulServiceRegistry: + return &consulServiceRegistryApplier{object: object.(*resource.ConsulServiceRegistry), baseApplier: baseApplier{client: client, timeout: timeout}} case resource.KindService: return &serviceApplier{object: object.(*resource.Service), baseApplier: baseApplier{client: client, timeout: timeout}} case resource.KindServiceInstance: @@ -103,6 +105,36 @@ func (mc *meshControllerApplier) Apply() error { } } +type consulServiceRegistryApplier struct { + baseApplier + object *resource.ConsulServiceRegistry +} + +func (c *consulServiceRegistryApplier) Apply() error { + ctx, cancelFunc := context.WithTimeout(context.Background(), c.timeout) + defer cancelFunc() + err := c.client.V1Alpha1().ConsulServiceRegistry().Create(ctx, c.object) + for { + switch { + case err == nil: + return nil + case meshclient.IsConflictError(err): + err = c.client.V1Alpha1().ConsulServiceRegistry().Patch(ctx, c.object) + if err != nil { + return errors.Wrapf(err, "update consulServiceRegistry %s", c.object.Name()) + } + case meshclient.IsNotFoundError(err): + err = c.client.V1Alpha1().ConsulServiceRegistry().Create(ctx, c.object) + if err != nil { + return errors.Wrapf(err, "create consulServiceRegistry %s", c.object.Name()) + } + default: + return errors.Wrapf(err, "apply consulServiceRegistry %s", c.object.Name()) + } + + } +} + type serviceApplier struct { baseApplier object *resource.Service diff --git a/emctl/cmd/client/command/delete/deleter.go b/emctl/cmd/client/command/delete/deleter.go index c69e6921..469b89e8 100644 --- a/emctl/cmd/client/command/delete/deleter.go +++ b/emctl/cmd/client/command/delete/deleter.go @@ -34,6 +34,8 @@ func WrapDeleterByMeshObject(object meta.MeshObject, switch object.Kind() { case resource.KindMeshController: return &meshControllerDeleter{object: object.(*resource.MeshController), baseDeleter: baseDeleter{client: client, timeout: timeout}} + case resource.KindConsulServiceRegistry: + return &consulServiceRegistryDeleter{object: object.(*resource.ConsulServiceRegistry), baseDeleter: baseDeleter{client: client, timeout: timeout}} case resource.KindService: return &serviceDeleter{object: object.(*resource.Service), baseDeleter: baseDeleter{client: client, timeout: timeout}} case resource.KindServiceInstance: @@ -82,6 +84,17 @@ func (s *meshControllerDeleter) Delete() error { return s.client.V1Alpha1().MeshController().Delete(ctx, s.object.Name()) } +type consulServiceRegistryDeleter struct { + baseDeleter + object *resource.ConsulServiceRegistry +} + +func (c *consulServiceRegistryDeleter) Delete() error { + ctx, cancelFunc := context.WithTimeout(context.Background(), c.timeout) + defer cancelFunc() + return c.client.V1Alpha1().ConsulServiceRegistry().Delete(ctx, c.object.Name()) +} + type serviceDeleter struct { baseDeleter object *resource.Service diff --git a/emctl/cmd/client/command/get/getter.go b/emctl/cmd/client/command/get/getter.go index fde3b433..916c4e36 100644 --- a/emctl/cmd/client/command/get/getter.go +++ b/emctl/cmd/client/command/get/getter.go @@ -38,6 +38,8 @@ func WrapGetterByMeshObject(object meta.MeshObject, switch object.Kind() { case resource.KindMeshController: return &meshControllerGetter{object: object.(*resource.MeshController), baseGetter: base} + case resource.KindConsulServiceRegistry: + return &consulServiceRegistryGetter{object: object.(*resource.ConsulServiceRegistry), baseGetter: base} case resource.KindService: return &serviceGetter{object: object.(*resource.Service), baseGetter: base} case resource.KindServiceInstance: @@ -108,6 +110,37 @@ func (s *meshControllerGetter) Get() ([]meta.MeshObject, error) { return objects, nil } +type consulServiceRegistryGetter struct { + baseGetter + object *resource.ConsulServiceRegistry +} + +func (s *consulServiceRegistryGetter) Get() ([]meta.MeshObject, error) { + ctx, cancelFunc := context.WithTimeout(context.Background(), s.timeout) + defer cancelFunc() + + if s.object.Name() != "" { + consulServiceRegistry, err := s.client.V1Alpha1().ConsulServiceRegistry().Get(ctx, s.object.Name()) + if err != nil { + return nil, err + } + + return []meta.MeshObject{consulServiceRegistry}, nil + } + + consulServiceRegistrys, err := s.client.V1Alpha1().ConsulServiceRegistry().List(ctx) + if err != nil { + return nil, err + } + + objects := make([]meta.MeshObject, len(consulServiceRegistrys)) + for i := range consulServiceRegistrys { + objects[i] = consulServiceRegistrys[i] + } + + return objects, nil +} + type serviceGetter struct { baseGetter object *resource.Service diff --git a/emctl/cmd/client/command/meshclient/consts.go b/emctl/cmd/client/command/meshclient/consts.go index e1110fa6..87b58e5b 100644 --- a/emctl/cmd/client/command/meshclient/consts.go +++ b/emctl/cmd/client/command/meshclient/consts.go @@ -26,6 +26,12 @@ const ( // MeshControllerURL is the mesh controller path. MeshControllerURL = apiURL + "/objects/%s" + // ConsulServiceRegistrysURL is the consul service registry path. + ConsulServiceRegistrysURL = apiURL + "/objects" + + // ConsulServiceRegistryURL is the consul service registry path. + ConsulServiceRegistryURL = apiURL + "/objects/%s" + // MeshTenantsURL is the mesh tenant prefix. MeshTenantsURL = apiURL + "/mesh/tenants" diff --git a/emctl/cmd/client/command/meshclient/consulserviceregistry.go b/emctl/cmd/client/command/meshclient/consulserviceregistry.go new file mode 100644 index 00000000..f9af7b6d --- /dev/null +++ b/emctl/cmd/client/command/meshclient/consulserviceregistry.go @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2017, MegaEase + * All rights reserved. + * + * Licensed 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 meshclient + +import ( + "context" + "fmt" + "net/http" + + "github.com/megaease/easemeshctl/cmd/client/resource" + "github.com/megaease/easemeshctl/cmd/common/client" + "gopkg.in/yaml.v2" + + "github.com/pkg/errors" +) + +type consulServiceRegistryGetter struct { + client *meshClient +} + +func (t *consulServiceRegistryGetter) ConsulServiceRegistry() ConsulServiceRegistryInterface { + return &consulServiceRegistryInterface{client: t.client} +} + +type consulServiceRegistryInterface struct { + client *meshClient +} + +func (t *consulServiceRegistryInterface) Get(ctx context.Context, consulServiceRegistryID string) (*resource.ConsulServiceRegistry, error) { + url := fmt.Sprintf("http://"+t.client.server+ConsulServiceRegistryURL, consulServiceRegistryID) + re, err := client.NewHTTPJSON(). + GetByContext(ctx, url, nil, nil). + HandleResponse(func(b []byte, statusCode int) (interface{}, error) { + if statusCode == http.StatusNotFound { + return nil, errors.Wrapf(NotFoundError, "get consulServiceRegistry %s", consulServiceRegistryID) + } + + if statusCode >= 300 { + return nil, errors.Errorf("call %s failed: return status code: %d text: %s", url, statusCode, string(b)) + } + consulServiceRegistry := &resource.ConsulServiceRegistryV1Alpha1{} + err := yaml.Unmarshal(b, consulServiceRegistry) + if err != nil { + return nil, errors.Wrap(err, "unmarshal data to ConsulServiceRegistry") + } + return resource.ToConsulServiceRegistry(consulServiceRegistry), nil + }) + if err != nil { + return nil, err + } + + return re.(*resource.ConsulServiceRegistry), nil +} + +func (t *consulServiceRegistryInterface) Patch(ctx context.Context, consulServiceRegistry *resource.ConsulServiceRegistry) error { + url := fmt.Sprintf("http://"+t.client.server+ConsulServiceRegistryURL, consulServiceRegistry.Name()) + update, err := yaml.Marshal(consulServiceRegistry.ToV1Alpha1()) + if err != nil { + return fmt.Errorf("marshal %#v to yaml failed: %v", consulServiceRegistry, err) + } + + _, err = client.NewHTTPJSON(). + PutByContext(ctx, url, update, nil). + HandleResponse(func(b []byte, statusCode int) (interface{}, error) { + if statusCode == http.StatusNotFound { + return nil, errors.Wrapf(NotFoundError, "patch consulServiceRegistry %s", consulServiceRegistry.Name()) + } + + if statusCode < 300 && statusCode >= 200 { + return nil, nil + } + return nil, errors.Errorf("call PUT %s failed, return statuscode %d text %s", url, statusCode, string(b)) + }) + + return err +} + +func (t *consulServiceRegistryInterface) Create(ctx context.Context, consulServiceRegistry *resource.ConsulServiceRegistry) error { + url := fmt.Sprintf("http://" + t.client.server + ConsulServiceRegistrysURL) + create, err := yaml.Marshal(consulServiceRegistry.ToV1Alpha1()) + if err != nil { + return fmt.Errorf("marshal %#v to yaml failed: %v", consulServiceRegistry, err) + } + + _, err = client.NewHTTPJSON(). + PostByContext(ctx, url, create, nil). + HandleResponse(func(b []byte, statusCode int) (interface{}, error) { + if statusCode == http.StatusConflict { + return nil, errors.Wrapf(ConflictError, "create consulServiceRegistry %s", consulServiceRegistry.Name()) + } + + if statusCode < 300 && statusCode >= 200 { + return nil, nil + } + return nil, errors.Errorf("call Post %s failed, return statuscode %d text %s", url, statusCode, string(b)) + }) + + return err +} + +func (t *consulServiceRegistryInterface) Delete(ctx context.Context, consulServiceRegistryID string) error { + url := fmt.Sprintf("http://"+t.client.server+ConsulServiceRegistryURL, consulServiceRegistryID) + _, err := client.NewHTTPJSON(). + DeleteByContext(ctx, url, nil, nil). + HandleResponse(func(b []byte, statusCode int) (interface{}, error) { + if statusCode == http.StatusNotFound { + return nil, NotFoundError + } + if statusCode < 300 && statusCode >= 200 { + return nil, nil + } + return nil, errors.Errorf("call DELETE %s failed, return statuscode %d text %s", url, statusCode, string(b)) + }) + return err +} + +func (t *consulServiceRegistryInterface) List(ctx context.Context) ([]*resource.ConsulServiceRegistry, error) { + url := fmt.Sprintf("http://" + t.client.server + ConsulServiceRegistrysURL) + result, err := client.NewHTTPJSON(). + GetByContext(ctx, url, nil, nil). + HandleResponse(func(b []byte, statusCode int) (interface{}, error) { + if statusCode == http.StatusNotFound { + return nil, errors.Wrap(NotFoundError, "list consulServiceRegistry") + } + + if statusCode >= 300 || statusCode < 200 { + return nil, errors.Errorf("call GET %s failed, return statuscode %d text %s", url, statusCode, string(b)) + } + + objects := []map[string]interface{}{} + err := yaml.Unmarshal(b, &objects) + if err != nil { + return nil, errors.Wrapf(err, "unmarshal objects") + } + + results := []*resource.ConsulServiceRegistry{} + for _, object := range objects { + if object["kind"] != resource.KindConsulServiceRegistry { + continue + } + + buff, err := yaml.Marshal(object) + if err != nil { + return nil, errors.Wrapf(err, "marshal %#v to yaml", object) + } + + consulServiceRegistry := &resource.ConsulServiceRegistryV1Alpha1{} + err = yaml.Unmarshal(buff, consulServiceRegistry) + if err != nil { + return nil, fmt.Errorf("unmarshal %s to yaml failed: %v", buff, err) + } + + results = append(results, resource.ToConsulServiceRegistry(consulServiceRegistry)) + } + return results, nil + }) + if err != nil { + return nil, err + } + return result.([]*resource.ConsulServiceRegistry), err +} diff --git a/emctl/cmd/client/command/meshclient/interface.go b/emctl/cmd/client/command/meshclient/interface.go index 62f6fe07..6817c3f4 100644 --- a/emctl/cmd/client/command/meshclient/interface.go +++ b/emctl/cmd/client/command/meshclient/interface.go @@ -31,6 +31,7 @@ type MeshClient interface { // V1Alpha1Interface is an interface that aggregates all resources accessor for the EaseMesh type V1Alpha1Interface interface { MeshControllerGetter + ConsulServiceRegistryGetter TenantGetter ServiceGetter ServiceInstanceGetter @@ -48,6 +49,11 @@ type MeshControllerGetter interface { MeshController() MeshControllerInterface } +// ConsulServiceRegistryGetter represents a consul service registry resource accessor +type ConsulServiceRegistryGetter interface { + ConsulServiceRegistry() ConsulServiceRegistryInterface +} + // TenantGetter represents a Tenant resource accessor type TenantGetter interface { Tenant() TenantInterface @@ -109,6 +115,15 @@ type MeshControllerInterface interface { List(context.Context) ([]*resource.MeshController, error) } +// ConsulServiceRegistryInterface captures the set of operations for interacting with the EaseMesh REST apis of the mesh controller resource. +type ConsulServiceRegistryInterface interface { + Get(context.Context, string) (*resource.ConsulServiceRegistry, error) + Patch(context.Context, *resource.ConsulServiceRegistry) error + Create(context.Context, *resource.ConsulServiceRegistry) error + Delete(context.Context, string) error + List(context.Context) ([]*resource.ConsulServiceRegistry, error) +} + // TenantInterface captures the set of operations for interacting with the EaseMesh REST apis of the tenant resource. type TenantInterface interface { Get(context.Context, string) (*resource.Tenant, error) diff --git a/emctl/cmd/client/command/meshclient/meshclient.go b/emctl/cmd/client/command/meshclient/meshclient.go index 3ab1129a..389bc682 100644 --- a/emctl/cmd/client/command/meshclient/meshclient.go +++ b/emctl/cmd/client/command/meshclient/meshclient.go @@ -28,6 +28,7 @@ func (m *meshClient) V1Alpha1() V1Alpha1Interface { type v1alpha1Interface struct { meshControllerGetter + consulServiceRegistryGetter loadbalanceGetter canaryGetter resilienceGetter @@ -46,17 +47,18 @@ var _ V1Alpha1Interface = &v1alpha1Interface{} func New(server string) MeshClient { client := &meshClient{server: server} alpha1 := v1alpha1Interface{ - meshControllerGetter: meshControllerGetter{client: client}, - loadbalanceGetter: loadbalanceGetter{client: client}, - canaryGetter: canaryGetter{client: client}, - resilienceGetter: resilienceGetter{client: client}, - tenantGetter: tenantGetter{client: client}, - observabilityGetter: observabilityGetter{client: client}, - serviceGetter: serviceGetter{client: client}, - serviceInstanceGetter: serviceInstanceGetter{client: client}, - ingressGetter: ingressGetter{client: client}, - customResourceKindGetter: customResourceKindGetter{client: client}, - customResourceGetter: customResourceGetter{client: client}, + meshControllerGetter: meshControllerGetter{client: client}, + consulServiceRegistryGetter: consulServiceRegistryGetter{client: client}, + loadbalanceGetter: loadbalanceGetter{client: client}, + canaryGetter: canaryGetter{client: client}, + resilienceGetter: resilienceGetter{client: client}, + tenantGetter: tenantGetter{client: client}, + observabilityGetter: observabilityGetter{client: client}, + serviceGetter: serviceGetter{client: client}, + serviceInstanceGetter: serviceInstanceGetter{client: client}, + ingressGetter: ingressGetter{client: client}, + customResourceKindGetter: customResourceKindGetter{client: client}, + customResourceGetter: customResourceGetter{client: client}, } client.v1Alpha1 = &alpha1 return client diff --git a/emctl/cmd/client/command/meshclient/meshcontroller.go b/emctl/cmd/client/command/meshclient/meshcontroller.go index c14b7c61..8f10e822 100644 --- a/emctl/cmd/client/command/meshclient/meshcontroller.go +++ b/emctl/cmd/client/command/meshclient/meshcontroller.go @@ -24,7 +24,7 @@ import ( "github.com/megaease/easemeshctl/cmd/client/resource" "github.com/megaease/easemeshctl/cmd/common/client" - "sigs.k8s.io/yaml" + "gopkg.in/yaml.v2" "github.com/pkg/errors" ) @@ -99,8 +99,6 @@ func (t *meshControllerInterface) Create(ctx context.Context, meshController *re } _, err = client.NewHTTPJSON(). - // FIXME: the standard RESTful URL of create resource is POST /v1/api/{resources} instead of POST /v1/api/{resources}/{id}. - // Current URL form should be corrected in the feature PostByContext(ctx, url, create, nil). HandleResponse(func(b []byte, statusCode int) (interface{}, error) { if statusCode == http.StatusConflict { diff --git a/emctl/cmd/client/resource/consulserviceregistry.go b/emctl/cmd/client/resource/consulserviceregistry.go new file mode 100644 index 00000000..e20b4ec6 --- /dev/null +++ b/emctl/cmd/client/resource/consulserviceregistry.go @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017, MegaEase + * All rights reserved. + * + * Licensed 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 resource + +import ( + "strings" + + "github.com/megaease/easemeshctl/cmd/client/command/printer" + "github.com/megaease/easemeshctl/cmd/client/resource/meta" +) + +type ( + + // ConsulServiceRegistry is the spec of ConsulServiceRegistry on Easegress. + ConsulServiceRegistry struct { + meta.MeshResource `yaml:",inline"` + ConsulServiceRegistrySpec `yaml:",inline"` + } + + // ConsulServiceRegistryV1Alpha1 is the v1alphv1 version of mesh controller. + ConsulServiceRegistryV1Alpha1 struct { + Kind string `yaml:"kind"` + Name string `yaml:"name"` + ConsulServiceRegistrySpec `yaml:",inline"` + } + + // ConsulServiceRegistrySpec is the admin config of mesh controller. + ConsulServiceRegistrySpec struct { + Address string `yaml:"address" jsonschema:"required"` + Scheme string `yaml:"scheme" jsonschema:"required,enum=http,enum=https"` + Datacenter string `yaml:"datacenter" jsonschema:"omitempty"` + Token string `yaml:"token" jsonschema:"omitempty"` + Namespace string `yaml:"namespace" jsonschema:"omitempty"` + SyncInterval string `yaml:"syncInterval" jsonschema:"required,format=duration"` + ServiceTags []string `yaml:"serviceTags" jsonschema:"omitempty"` + } +) + +var _ printer.TableObject = &ConsulServiceRegistry{} + +// Columns returns the columns of ConsulServiceRegistry. +func (mc *ConsulServiceRegistry) Columns() []*printer.TableColumn { + return []*printer.TableColumn{ + { + Name: "Address", + Value: mc.Address, + }, + { + Name: "SyncInterval", + Value: mc.SyncInterval, + }, + { + Name: "ServiceTags", + Value: strings.Join(mc.ServiceTags, ","), + }, + } +} + +// ToV1Alpha1 converts ConsulServiceRegistry resouce to v1alpha1. +func (mc *ConsulServiceRegistry) ToV1Alpha1() *ConsulServiceRegistryV1Alpha1 { + return &ConsulServiceRegistryV1Alpha1{ + Kind: mc.Kind(), + Name: mc.Name(), + ConsulServiceRegistrySpec: mc.ConsulServiceRegistrySpec, + } +} + +// ToConsulServiceRegistry converts a ConsulServiceRegistryV1Alpha1 resouce to a ConsulServiceRegistry resource. +func ToConsulServiceRegistry(consulServiceRegistry *ConsulServiceRegistryV1Alpha1) *ConsulServiceRegistry { + return &ConsulServiceRegistry{ + MeshResource: NewMeshResource(DefaultAPIVersion, consulServiceRegistry.Kind, consulServiceRegistry.Name), + ConsulServiceRegistrySpec: consulServiceRegistry.ConsulServiceRegistrySpec, + } +} diff --git a/emctl/cmd/client/resource/resource.go b/emctl/cmd/client/resource/resource.go index ff1f7a30..7ae6a746 100644 --- a/emctl/cmd/client/resource/resource.go +++ b/emctl/cmd/client/resource/resource.go @@ -48,6 +48,9 @@ const ( // KindMeshController is mesh controller kind of the EaseMesh control plane. KindMeshController = "MeshController" + // KindConsulServiceRegistry is consul service registry kind of EaseMesh control plane. + KindConsulServiceRegistry = "ConsulServiceRegistry" + // KindService is service kind of the EaseMesh resource KindService = "Service" @@ -116,6 +119,10 @@ func (oc *objectCreator) new(kind meta.VersionKind, metaData meta.MetaData) (met return &MeshController{ MeshResource: NewMeshControllerResource(apiVersion, metaData.Name), }, nil + case KindConsulServiceRegistry: + return &ConsulServiceRegistry{ + MeshResource: NewConsulServiceRegistry(apiVersion, metaData.Name), + }, nil case KindService: return &Service{ MeshResource: NewServiceResource(apiVersion, metaData.Name), @@ -172,6 +179,11 @@ func NewMeshControllerResource(apiVersion, name string) meta.MeshResource { return NewMeshResource(apiVersion, KindMeshController, name) } +// NewConsulServiceRegistry returns a MeshResouce with the consul service registry kind. +func NewConsulServiceRegistry(apiVersion, name string) meta.MeshResource { + return NewMeshResource(apiVersion, KindConsulServiceRegistry, name) +} + // NewIngressResource returns a MeshResource with the ingress kind func NewIngressResource(apiVersion, name string) meta.MeshResource { return NewMeshResource(apiVersion, KindIngress, name) diff --git a/emctl/cmd/client/util/visitor.go b/emctl/cmd/client/util/visitor.go index 931de00e..7e20563c 100644 --- a/emctl/cmd/client/util/visitor.go +++ b/emctl/cmd/client/util/visitor.go @@ -298,6 +298,8 @@ func adaptCommandKind(kind string) string { switch low(kind) { case low(resource.KindMeshController): return resource.KindMeshController + case low(resource.KindConsulServiceRegistry): + return resource.KindConsulServiceRegistry case low(resource.KindService): return resource.KindService case low(resource.KindServiceInstance):