From 2c097c2f389df0538d6b03c9c68f3b2fbf451a05 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Tue, 31 Aug 2021 16:34:51 -0700 Subject: [PATCH 01/71] sdk: add host plugin protobufs and interface (#1473) * sdk: add host plugin protobufs and interface This adds the protobufs and interface for the upcoming plugin-based host feature for boundary, providing a common interface for building targets and host catalogs off of dynamic cloud data. * Fix signature formatting and separate out/flesh out descriptions * sdk: re-gen with protobuf v3.15.8 * sdk: update protobuf according to @talanknight's comments * Update internal/proto/local/plugin/host_catalog.proto Co-authored-by: Todd Knight * Add TODO for host catalog persisted data for size limits * sdk: move id to external_id and make protobuild * Move plugin interface to GRPC service This re-uses existing protobufs where possible, a few more messages have been created for persisted data and for the host response where it's important that we make it clear that the external ID is *not* the same ID as the calculated stable ID for the host within Boundary. * Update internal/proto/local/plugin/host_plugin_service.proto Co-authored-by: Todd Knight * Move proto target to plugin/proto, fix formatting Co-authored-by: Todd Knight --- .../local/plugin/host_plugin_service.proto | 174 ++ plugin/proto/host_plugin_service.pb.go | 1395 +++++++++++++++++ plugin/proto/host_plugin_service_grpc.pb.go | 337 ++++ 3 files changed, 1906 insertions(+) create mode 100644 internal/proto/local/plugin/host_plugin_service.proto create mode 100644 plugin/proto/host_plugin_service.pb.go create mode 100644 plugin/proto/host_plugin_service_grpc.pb.go diff --git a/internal/proto/local/plugin/host_plugin_service.proto b/internal/proto/local/plugin/host_plugin_service.proto new file mode 100644 index 000000000..ffcc794d4 --- /dev/null +++ b/internal/proto/local/plugin/host_plugin_service.proto @@ -0,0 +1,174 @@ +syntax = "proto3"; + +package plugin; + +option go_package = "github.com/hashicorp/boundary/plugin/proto;proto"; + +import "google/protobuf/struct.proto"; + +import "controller/api/resources/hostcatalogs/v1/host_catalog.proto"; +import "controller/api/resources/hostsets/v1/host_set.proto"; +import "controller/api/resources/hosts/v1/host.proto"; + +// HostPluginService describes the service for host plugins. +service HostPluginService { + // OnCreateCatalog is a hook that runs when a host catalog is + // created. + rpc OnCreateCatalog(OnCreateCatalogRequest) returns (OnCreateCatalogResponse); + + // OnUpdateCatalog is a hook that runs when a host catalog is + // updated. + rpc OnUpdateCatalog(OnUpdateCatalogRequest) returns (OnUpdateCatalogResponse); + + // OnDeleteCatalog is a hook that runs when a host catalog is + // deleted. + rpc OnDeleteCatalog(OnDeleteCatalogRequest) returns (OnDeleteCatalogResponse); + + // OnCreateSet is a hook that runs when a host set is created. + rpc OnCreateSet(OnCreateSetRequest) returns (OnCreateSetResponse); + + // OnUpdateSet is a hook that runs when a host set is updated. + rpc OnUpdateSet(OnUpdateSetRequest) returns (OnUpdateSetResponse); + + // OnDeleteSet is a hook that runs when a host set is deleted. + rpc OnDeleteSet(OnDeleteSetRequest) returns (OnDeleteSetResponse); + + // ListHosts looks up all the hosts in the provided host sets. + rpc ListHosts(ListHostsRequest) returns (ListHostsResponse); +} + +message OnCreateCatalogRequest { + // The host catalog to create. The request may contain optional + // secret data to help authenticate the request against a cloud + // API. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; +} + +message OnCreateCatalogResponse { + // Secret data to persist encrypted within Boundary. This should be + // used to store authentication data and other necessary + // configuration to be used in later hooks and calls. Returning an + // error from the call will cause this data to not be persisted. + HostCatalogPersisted persisted = 10; +} + +message OnUpdateCatalogRequest { + // The existing state of the catalog. + controller.api.resources.hostcatalogs.v1.HostCatalog current_catalog = 10; + + // The requested new state of the catalog. This field may contain + // optional secret data that may have been updated from old + // authentication data contained within the persisted state. + controller.api.resources.hostcatalogs.v1.HostCatalog new_catalog = 20; + + // The existing persisted secret data. + HostCatalogPersisted persisted = 30; +} + +message OnUpdateCatalogResponse { + // The updated secret data to persist encrypted within Boundary. + // It's important that this be returned if it existed previously, + // as the returned data overwrites the previously existing copy. If + // an error is returned, the update of the persisted data is + // aborted. + HostCatalogPersisted persisted = 10; +} + +message OnDeleteCatalogRequest { + // The existing state of the catalog to delete. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The existing persisted secret data. + HostCatalogPersisted persisted = 20; +} + +message OnDeleteCatalogResponse {} + +message OnCreateSetRequest { + // The host catalog that the set belongs to. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The host set to create. + controller.api.resources.hostsets.v1.HostSet set = 20; + + // The persisted data for the host catalog that the set belongs to. + HostCatalogPersisted persisted = 30; +} + +message OnCreateSetResponse {} + +message OnUpdateSetRequest { + // The host catalog that the set belongs to. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The existing state of the host set. + controller.api.resources.hostsets.v1.HostSet current_set = 20; + + // The requested new state of the host set. + controller.api.resources.hostsets.v1.HostSet new_set = 30; + + // The persisted data for the host catalog that the set belongs to. + HostCatalogPersisted persisted = 40; +} + +message OnUpdateSetResponse {} + +message OnDeleteSetRequest { + // The host catalog that the set belongs to. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The host set to delete. + controller.api.resources.hostsets.v1.HostSet current_set = 20; + + // The persisted data for the host catalog that the set belongs to. + HostCatalogPersisted persisted = 30; +} + +message OnDeleteSetResponse {} + +message ListHostsRequest { + // The host catalog that the supplied host sets belong to. + controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The host sets to look up hosts for. + repeated controller.api.resources.hostsets.v1.HostSet sets = 20; + + // The persisted data for the host catalog that the supplied host + // sets belong to. + HostCatalogPersisted persisted = 30; +} + +message ListHostsResponse { + // The hosts to return. + repeated ListHostsResponseHost hosts = 10; +} + +message ListHostsResponseHost { + // Required. A stable identifier for this host. This field is used + // to generate a stable ID for the host within Boundary and is + // included in audit logs. It should be set to something unique and + // useful, ie: a compute instance ID. + string external_id = 10; + + // Required. The primary address for the host. + string address = 20; + + // Optional. Provider-specific metadata that is applicable to this + // host. Example: host descriptions, tags, alternate network + // addresses, etc. + google.protobuf.Struct attributes = 100; +} + +// HostCatalogPersisted represents state persisted between host +// catalog calls. Its intended purpose is to store authentication data +// required by the plugin to make calls to its respective cloud API. +// +// The data stored in this message is encrypted at-rest by Boundary +// and never returned to the end user. +// +// TODO: Add a size limit to this data before we export the plugin +// SDK. +message HostCatalogPersisted { + // The persisted data. + google.protobuf.Struct data = 100; +} diff --git a/plugin/proto/host_plugin_service.pb.go b/plugin/proto/host_plugin_service.pb.go new file mode 100644 index 000000000..db6fa8fd2 --- /dev/null +++ b/plugin/proto/host_plugin_service.pb.go @@ -0,0 +1,1395 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.15.8 +// source: plugin/host_plugin_service.proto + +package proto + +import ( + hostcatalogs "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts" + hostsets "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + 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 OnCreateCatalogRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The host catalog to create. The request may contain optional + // secret data to help authenticate the request against a cloud + // API. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` +} + +func (x *OnCreateCatalogRequest) Reset() { + *x = OnCreateCatalogRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnCreateCatalogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnCreateCatalogRequest) ProtoMessage() {} + +func (x *OnCreateCatalogRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_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 OnCreateCatalogRequest.ProtoReflect.Descriptor instead. +func (*OnCreateCatalogRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{0} +} + +func (x *OnCreateCatalogRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +type OnCreateCatalogResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Secret data to persist encrypted within Boundary. This should be + // used to store authentication data and other necessary + // configuration to be used in later hooks and calls. Returning an + // error from the call will cause this data to not be persisted. + Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnCreateCatalogResponse) Reset() { + *x = OnCreateCatalogResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnCreateCatalogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnCreateCatalogResponse) ProtoMessage() {} + +func (x *OnCreateCatalogResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_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 OnCreateCatalogResponse.ProtoReflect.Descriptor instead. +func (*OnCreateCatalogResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{1} +} + +func (x *OnCreateCatalogResponse) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnUpdateCatalogRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The existing state of the catalog. + CurrentCatalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=current_catalog,json=currentCatalog,proto3" json:"current_catalog,omitempty"` + // The requested new state of the catalog. This field may contain + // optional secret data that may have been updated from old + // authentication data contained within the persisted state. + NewCatalog *hostcatalogs.HostCatalog `protobuf:"bytes,20,opt,name=new_catalog,json=newCatalog,proto3" json:"new_catalog,omitempty"` + // The existing persisted secret data. + Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnUpdateCatalogRequest) Reset() { + *x = OnUpdateCatalogRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnUpdateCatalogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnUpdateCatalogRequest) ProtoMessage() {} + +func (x *OnUpdateCatalogRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_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 OnUpdateCatalogRequest.ProtoReflect.Descriptor instead. +func (*OnUpdateCatalogRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{2} +} + +func (x *OnUpdateCatalogRequest) GetCurrentCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.CurrentCatalog + } + return nil +} + +func (x *OnUpdateCatalogRequest) GetNewCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.NewCatalog + } + return nil +} + +func (x *OnUpdateCatalogRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnUpdateCatalogResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The updated secret data to persist encrypted within Boundary. + // It's important that this be returned if it existed previously, + // as the returned data overwrites the previously existing copy. If + // an error is returned, the update of the persisted data is + // aborted. + Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnUpdateCatalogResponse) Reset() { + *x = OnUpdateCatalogResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnUpdateCatalogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnUpdateCatalogResponse) ProtoMessage() {} + +func (x *OnUpdateCatalogResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_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 OnUpdateCatalogResponse.ProtoReflect.Descriptor instead. +func (*OnUpdateCatalogResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{3} +} + +func (x *OnUpdateCatalogResponse) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnDeleteCatalogRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The existing state of the catalog to delete. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The existing persisted secret data. + Persisted *HostCatalogPersisted `protobuf:"bytes,20,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnDeleteCatalogRequest) Reset() { + *x = OnDeleteCatalogRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnDeleteCatalogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnDeleteCatalogRequest) ProtoMessage() {} + +func (x *OnDeleteCatalogRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_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 OnDeleteCatalogRequest.ProtoReflect.Descriptor instead. +func (*OnDeleteCatalogRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{4} +} + +func (x *OnDeleteCatalogRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +func (x *OnDeleteCatalogRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnDeleteCatalogResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OnDeleteCatalogResponse) Reset() { + *x = OnDeleteCatalogResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnDeleteCatalogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnDeleteCatalogResponse) ProtoMessage() {} + +func (x *OnDeleteCatalogResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[5] + 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 OnDeleteCatalogResponse.ProtoReflect.Descriptor instead. +func (*OnDeleteCatalogResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{5} +} + +type OnCreateSetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The host catalog that the set belongs to. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The host set to create. + Set *hostsets.HostSet `protobuf:"bytes,20,opt,name=set,proto3" json:"set,omitempty"` + // The persisted data for the host catalog that the set belongs to. + Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnCreateSetRequest) Reset() { + *x = OnCreateSetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnCreateSetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnCreateSetRequest) ProtoMessage() {} + +func (x *OnCreateSetRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[6] + 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 OnCreateSetRequest.ProtoReflect.Descriptor instead. +func (*OnCreateSetRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{6} +} + +func (x *OnCreateSetRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +func (x *OnCreateSetRequest) GetSet() *hostsets.HostSet { + if x != nil { + return x.Set + } + return nil +} + +func (x *OnCreateSetRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnCreateSetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OnCreateSetResponse) Reset() { + *x = OnCreateSetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnCreateSetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnCreateSetResponse) ProtoMessage() {} + +func (x *OnCreateSetResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[7] + 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 OnCreateSetResponse.ProtoReflect.Descriptor instead. +func (*OnCreateSetResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{7} +} + +type OnUpdateSetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The host catalog that the set belongs to. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The existing state of the host set. + CurrentSet *hostsets.HostSet `protobuf:"bytes,20,opt,name=current_set,json=currentSet,proto3" json:"current_set,omitempty"` + // The requested new state of the host set. + NewSet *hostsets.HostSet `protobuf:"bytes,30,opt,name=new_set,json=newSet,proto3" json:"new_set,omitempty"` + // The persisted data for the host catalog that the set belongs to. + Persisted *HostCatalogPersisted `protobuf:"bytes,40,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnUpdateSetRequest) Reset() { + *x = OnUpdateSetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnUpdateSetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnUpdateSetRequest) ProtoMessage() {} + +func (x *OnUpdateSetRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[8] + 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 OnUpdateSetRequest.ProtoReflect.Descriptor instead. +func (*OnUpdateSetRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{8} +} + +func (x *OnUpdateSetRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +func (x *OnUpdateSetRequest) GetCurrentSet() *hostsets.HostSet { + if x != nil { + return x.CurrentSet + } + return nil +} + +func (x *OnUpdateSetRequest) GetNewSet() *hostsets.HostSet { + if x != nil { + return x.NewSet + } + return nil +} + +func (x *OnUpdateSetRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnUpdateSetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OnUpdateSetResponse) Reset() { + *x = OnUpdateSetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnUpdateSetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnUpdateSetResponse) ProtoMessage() {} + +func (x *OnUpdateSetResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[9] + 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 OnUpdateSetResponse.ProtoReflect.Descriptor instead. +func (*OnUpdateSetResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{9} +} + +type OnDeleteSetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The host catalog that the set belongs to. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The host set to delete. + CurrentSet *hostsets.HostSet `protobuf:"bytes,20,opt,name=current_set,json=currentSet,proto3" json:"current_set,omitempty"` + // The persisted data for the host catalog that the set belongs to. + Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *OnDeleteSetRequest) Reset() { + *x = OnDeleteSetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnDeleteSetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnDeleteSetRequest) ProtoMessage() {} + +func (x *OnDeleteSetRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[10] + 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 OnDeleteSetRequest.ProtoReflect.Descriptor instead. +func (*OnDeleteSetRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{10} +} + +func (x *OnDeleteSetRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +func (x *OnDeleteSetRequest) GetCurrentSet() *hostsets.HostSet { + if x != nil { + return x.CurrentSet + } + return nil +} + +func (x *OnDeleteSetRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type OnDeleteSetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OnDeleteSetResponse) Reset() { + *x = OnDeleteSetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnDeleteSetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnDeleteSetResponse) ProtoMessage() {} + +func (x *OnDeleteSetResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[11] + 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 OnDeleteSetResponse.ProtoReflect.Descriptor instead. +func (*OnDeleteSetResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{11} +} + +type ListHostsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The host catalog that the supplied host sets belong to. + Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The host sets to look up hosts for. + Sets []*hostsets.HostSet `protobuf:"bytes,20,rep,name=sets,proto3" json:"sets,omitempty"` + // The persisted data for the host catalog that the supplied host + // sets belong to. + Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` +} + +func (x *ListHostsRequest) Reset() { + *x = ListHostsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListHostsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListHostsRequest) ProtoMessage() {} + +func (x *ListHostsRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[12] + 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 ListHostsRequest.ProtoReflect.Descriptor instead. +func (*ListHostsRequest) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{12} +} + +func (x *ListHostsRequest) GetCatalog() *hostcatalogs.HostCatalog { + if x != nil { + return x.Catalog + } + return nil +} + +func (x *ListHostsRequest) GetSets() []*hostsets.HostSet { + if x != nil { + return x.Sets + } + return nil +} + +func (x *ListHostsRequest) GetPersisted() *HostCatalogPersisted { + if x != nil { + return x.Persisted + } + return nil +} + +type ListHostsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hosts to return. + Hosts []*ListHostsResponseHost `protobuf:"bytes,10,rep,name=hosts,proto3" json:"hosts,omitempty"` +} + +func (x *ListHostsResponse) Reset() { + *x = ListHostsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListHostsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListHostsResponse) ProtoMessage() {} + +func (x *ListHostsResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[13] + 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 ListHostsResponse.ProtoReflect.Descriptor instead. +func (*ListHostsResponse) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{13} +} + +func (x *ListHostsResponse) GetHosts() []*ListHostsResponseHost { + if x != nil { + return x.Hosts + } + return nil +} + +type ListHostsResponseHost struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. A stable identifier for this host. This field is used + // to generate a stable ID for the host within Boundary and is + // included in audit logs. It should be set to something unique and + // useful, ie: a compute instance ID. + ExternalId string `protobuf:"bytes,10,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + // Required. The primary address for the host. + Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty"` + // Optional. Provider-specific metadata that is applicable to this + // host. Example: host descriptions, tags, alternate network + // addresses, etc. + Attributes *structpb.Struct `protobuf:"bytes,100,opt,name=attributes,proto3" json:"attributes,omitempty"` +} + +func (x *ListHostsResponseHost) Reset() { + *x = ListHostsResponseHost{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListHostsResponseHost) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListHostsResponseHost) ProtoMessage() {} + +func (x *ListHostsResponseHost) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[14] + 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 ListHostsResponseHost.ProtoReflect.Descriptor instead. +func (*ListHostsResponseHost) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{14} +} + +func (x *ListHostsResponseHost) GetExternalId() string { + if x != nil { + return x.ExternalId + } + return "" +} + +func (x *ListHostsResponseHost) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *ListHostsResponseHost) GetAttributes() *structpb.Struct { + if x != nil { + return x.Attributes + } + return nil +} + +// HostCatalogPersisted represents state persisted between host +// catalog calls. Its intended purpose is to store authentication data +// required by the plugin to make calls to its respective cloud API. +// +// The data stored in this message is encrypted at-rest by Boundary +// and never returned to the end user. +// +// TODO: Add a size limit to this data before we export the plugin +// SDK. +type HostCatalogPersisted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The persisted data. + Data *structpb.Struct `protobuf:"bytes,100,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *HostCatalogPersisted) Reset() { + *x = HostCatalogPersisted{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_host_plugin_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostCatalogPersisted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostCatalogPersisted) ProtoMessage() {} + +func (x *HostCatalogPersisted) ProtoReflect() protoreflect.Message { + mi := &file_plugin_host_plugin_service_proto_msgTypes[15] + 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 HostCatalogPersisted.ProtoReflect.Descriptor instead. +func (*HostCatalogPersisted) Descriptor() ([]byte, []int) { + return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{15} +} + +func (x *HostCatalogPersisted) GetData() *structpb.Struct { + if x != nil { + return x.Data + } + return nil +} + +var File_plugin_host_plugin_service_proto protoreflect.FileDescriptor + +var file_plugin_host_plugin_service_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 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, 0x3b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, + 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x33, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, + 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x73, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, + 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x69, 0x0a, 0x16, 0x4f, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, + 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x22, 0x55, 0x0a, 0x17, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, + 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, + 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x8c, 0x02, 0x0a, 0x16, 0x4f, + 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x56, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, + 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, + 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x17, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, + 0x22, 0xa5, 0x01, 0x0a, 0x16, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, 0x09, + 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x4f, 0x6e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3f, 0x0a, 0x03, 0x73, + 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x09, + 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xb9, 0x02, 0x0a, 0x12, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x46, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x53, 0x65, 0x74, 0x12, + 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x28, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, + 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, + 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, + 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, + 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, + 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe2, 0x01, + 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, + 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, + 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, + 0x52, 0x04, 0x73, 0x65, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, + 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x22, 0x48, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x8b, 0x01, 0x0a, + 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, + 0x64, 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, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, 0x32, + 0xa9, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x1e, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, + 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4c, 0x69, 0x73, + 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_plugin_host_plugin_service_proto_rawDescOnce sync.Once + file_plugin_host_plugin_service_proto_rawDescData = file_plugin_host_plugin_service_proto_rawDesc +) + +func file_plugin_host_plugin_service_proto_rawDescGZIP() []byte { + file_plugin_host_plugin_service_proto_rawDescOnce.Do(func() { + file_plugin_host_plugin_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_plugin_host_plugin_service_proto_rawDescData) + }) + return file_plugin_host_plugin_service_proto_rawDescData +} + +var file_plugin_host_plugin_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_plugin_host_plugin_service_proto_goTypes = []interface{}{ + (*OnCreateCatalogRequest)(nil), // 0: plugin.OnCreateCatalogRequest + (*OnCreateCatalogResponse)(nil), // 1: plugin.OnCreateCatalogResponse + (*OnUpdateCatalogRequest)(nil), // 2: plugin.OnUpdateCatalogRequest + (*OnUpdateCatalogResponse)(nil), // 3: plugin.OnUpdateCatalogResponse + (*OnDeleteCatalogRequest)(nil), // 4: plugin.OnDeleteCatalogRequest + (*OnDeleteCatalogResponse)(nil), // 5: plugin.OnDeleteCatalogResponse + (*OnCreateSetRequest)(nil), // 6: plugin.OnCreateSetRequest + (*OnCreateSetResponse)(nil), // 7: plugin.OnCreateSetResponse + (*OnUpdateSetRequest)(nil), // 8: plugin.OnUpdateSetRequest + (*OnUpdateSetResponse)(nil), // 9: plugin.OnUpdateSetResponse + (*OnDeleteSetRequest)(nil), // 10: plugin.OnDeleteSetRequest + (*OnDeleteSetResponse)(nil), // 11: plugin.OnDeleteSetResponse + (*ListHostsRequest)(nil), // 12: plugin.ListHostsRequest + (*ListHostsResponse)(nil), // 13: plugin.ListHostsResponse + (*ListHostsResponseHost)(nil), // 14: plugin.ListHostsResponseHost + (*HostCatalogPersisted)(nil), // 15: plugin.HostCatalogPersisted + (*hostcatalogs.HostCatalog)(nil), // 16: controller.api.resources.hostcatalogs.v1.HostCatalog + (*hostsets.HostSet)(nil), // 17: controller.api.resources.hostsets.v1.HostSet + (*structpb.Struct)(nil), // 18: google.protobuf.Struct +} +var file_plugin_host_plugin_service_proto_depIdxs = []int32{ + 16, // 0: plugin.OnCreateCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 1: plugin.OnCreateCatalogResponse.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 2: plugin.OnUpdateCatalogRequest.current_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 16, // 3: plugin.OnUpdateCatalogRequest.new_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 4: plugin.OnUpdateCatalogRequest.persisted:type_name -> plugin.HostCatalogPersisted + 15, // 5: plugin.OnUpdateCatalogResponse.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 6: plugin.OnDeleteCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 7: plugin.OnDeleteCatalogRequest.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 8: plugin.OnCreateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 9: plugin.OnCreateSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 10: plugin.OnCreateSetRequest.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 11: plugin.OnUpdateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 12: plugin.OnUpdateSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 17, // 13: plugin.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 14: plugin.OnUpdateSetRequest.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 15: plugin.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 16: plugin.OnDeleteSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 17: plugin.OnDeleteSetRequest.persisted:type_name -> plugin.HostCatalogPersisted + 16, // 18: plugin.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 19: plugin.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 20: plugin.ListHostsRequest.persisted:type_name -> plugin.HostCatalogPersisted + 14, // 21: plugin.ListHostsResponse.hosts:type_name -> plugin.ListHostsResponseHost + 18, // 22: plugin.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct + 18, // 23: plugin.HostCatalogPersisted.data:type_name -> google.protobuf.Struct + 0, // 24: plugin.HostPluginService.OnCreateCatalog:input_type -> plugin.OnCreateCatalogRequest + 2, // 25: plugin.HostPluginService.OnUpdateCatalog:input_type -> plugin.OnUpdateCatalogRequest + 4, // 26: plugin.HostPluginService.OnDeleteCatalog:input_type -> plugin.OnDeleteCatalogRequest + 6, // 27: plugin.HostPluginService.OnCreateSet:input_type -> plugin.OnCreateSetRequest + 8, // 28: plugin.HostPluginService.OnUpdateSet:input_type -> plugin.OnUpdateSetRequest + 10, // 29: plugin.HostPluginService.OnDeleteSet:input_type -> plugin.OnDeleteSetRequest + 12, // 30: plugin.HostPluginService.ListHosts:input_type -> plugin.ListHostsRequest + 1, // 31: plugin.HostPluginService.OnCreateCatalog:output_type -> plugin.OnCreateCatalogResponse + 3, // 32: plugin.HostPluginService.OnUpdateCatalog:output_type -> plugin.OnUpdateCatalogResponse + 5, // 33: plugin.HostPluginService.OnDeleteCatalog:output_type -> plugin.OnDeleteCatalogResponse + 7, // 34: plugin.HostPluginService.OnCreateSet:output_type -> plugin.OnCreateSetResponse + 9, // 35: plugin.HostPluginService.OnUpdateSet:output_type -> plugin.OnUpdateSetResponse + 11, // 36: plugin.HostPluginService.OnDeleteSet:output_type -> plugin.OnDeleteSetResponse + 13, // 37: plugin.HostPluginService.ListHosts:output_type -> plugin.ListHostsResponse + 31, // [31:38] is the sub-list for method output_type + 24, // [24:31] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name +} + +func init() { file_plugin_host_plugin_service_proto_init() } +func file_plugin_host_plugin_service_proto_init() { + if File_plugin_host_plugin_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_plugin_host_plugin_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnCreateCatalogRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnCreateCatalogResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnUpdateCatalogRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnUpdateCatalogResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnDeleteCatalogRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnDeleteCatalogResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnCreateSetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnCreateSetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnUpdateSetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnUpdateSetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnDeleteSetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OnDeleteSetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListHostsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListHostsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListHostsResponseHost); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_host_plugin_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostCatalogPersisted); 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_plugin_host_plugin_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 16, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_plugin_host_plugin_service_proto_goTypes, + DependencyIndexes: file_plugin_host_plugin_service_proto_depIdxs, + MessageInfos: file_plugin_host_plugin_service_proto_msgTypes, + }.Build() + File_plugin_host_plugin_service_proto = out.File + file_plugin_host_plugin_service_proto_rawDesc = nil + file_plugin_host_plugin_service_proto_goTypes = nil + file_plugin_host_plugin_service_proto_depIdxs = nil +} diff --git a/plugin/proto/host_plugin_service_grpc.pb.go b/plugin/proto/host_plugin_service_grpc.pb.go new file mode 100644 index 000000000..e6fe33224 --- /dev/null +++ b/plugin/proto/host_plugin_service_grpc.pb.go @@ -0,0 +1,337 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// HostPluginServiceClient is the client API for HostPluginService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HostPluginServiceClient interface { + // OnCreateCatalog is a hook that runs when a host catalog is + // created. + OnCreateCatalog(ctx context.Context, in *OnCreateCatalogRequest, opts ...grpc.CallOption) (*OnCreateCatalogResponse, error) + // OnUpdateCatalog is a hook that runs when a host catalog is + // updated. + OnUpdateCatalog(ctx context.Context, in *OnUpdateCatalogRequest, opts ...grpc.CallOption) (*OnUpdateCatalogResponse, error) + // OnDeleteCatalog is a hook that runs when a host catalog is + // deleted. + OnDeleteCatalog(ctx context.Context, in *OnDeleteCatalogRequest, opts ...grpc.CallOption) (*OnDeleteCatalogResponse, error) + // OnCreateSet is a hook that runs when a host set is created. + OnCreateSet(ctx context.Context, in *OnCreateSetRequest, opts ...grpc.CallOption) (*OnCreateSetResponse, error) + // OnUpdateSet is a hook that runs when a host set is updated. + OnUpdateSet(ctx context.Context, in *OnUpdateSetRequest, opts ...grpc.CallOption) (*OnUpdateSetResponse, error) + // OnDeleteSet is a hook that runs when a host set is deleted. + OnDeleteSet(ctx context.Context, in *OnDeleteSetRequest, opts ...grpc.CallOption) (*OnDeleteSetResponse, error) + // ListHosts looks up all the hosts in the provided host sets. + ListHosts(ctx context.Context, in *ListHostsRequest, opts ...grpc.CallOption) (*ListHostsResponse, error) +} + +type hostPluginServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewHostPluginServiceClient(cc grpc.ClientConnInterface) HostPluginServiceClient { + return &hostPluginServiceClient{cc} +} + +func (c *hostPluginServiceClient) OnCreateCatalog(ctx context.Context, in *OnCreateCatalogRequest, opts ...grpc.CallOption) (*OnCreateCatalogResponse, error) { + out := new(OnCreateCatalogResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnCreateCatalog", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) OnUpdateCatalog(ctx context.Context, in *OnUpdateCatalogRequest, opts ...grpc.CallOption) (*OnUpdateCatalogResponse, error) { + out := new(OnUpdateCatalogResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnUpdateCatalog", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) OnDeleteCatalog(ctx context.Context, in *OnDeleteCatalogRequest, opts ...grpc.CallOption) (*OnDeleteCatalogResponse, error) { + out := new(OnDeleteCatalogResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnDeleteCatalog", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) OnCreateSet(ctx context.Context, in *OnCreateSetRequest, opts ...grpc.CallOption) (*OnCreateSetResponse, error) { + out := new(OnCreateSetResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnCreateSet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) OnUpdateSet(ctx context.Context, in *OnUpdateSetRequest, opts ...grpc.CallOption) (*OnUpdateSetResponse, error) { + out := new(OnUpdateSetResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnUpdateSet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) OnDeleteSet(ctx context.Context, in *OnDeleteSetRequest, opts ...grpc.CallOption) (*OnDeleteSetResponse, error) { + out := new(OnDeleteSetResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnDeleteSet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hostPluginServiceClient) ListHosts(ctx context.Context, in *ListHostsRequest, opts ...grpc.CallOption) (*ListHostsResponse, error) { + out := new(ListHostsResponse) + err := c.cc.Invoke(ctx, "/plugin.HostPluginService/ListHosts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HostPluginServiceServer is the server API for HostPluginService service. +// All implementations must embed UnimplementedHostPluginServiceServer +// for forward compatibility +type HostPluginServiceServer interface { + // OnCreateCatalog is a hook that runs when a host catalog is + // created. + OnCreateCatalog(context.Context, *OnCreateCatalogRequest) (*OnCreateCatalogResponse, error) + // OnUpdateCatalog is a hook that runs when a host catalog is + // updated. + OnUpdateCatalog(context.Context, *OnUpdateCatalogRequest) (*OnUpdateCatalogResponse, error) + // OnDeleteCatalog is a hook that runs when a host catalog is + // deleted. + OnDeleteCatalog(context.Context, *OnDeleteCatalogRequest) (*OnDeleteCatalogResponse, error) + // OnCreateSet is a hook that runs when a host set is created. + OnCreateSet(context.Context, *OnCreateSetRequest) (*OnCreateSetResponse, error) + // OnUpdateSet is a hook that runs when a host set is updated. + OnUpdateSet(context.Context, *OnUpdateSetRequest) (*OnUpdateSetResponse, error) + // OnDeleteSet is a hook that runs when a host set is deleted. + OnDeleteSet(context.Context, *OnDeleteSetRequest) (*OnDeleteSetResponse, error) + // ListHosts looks up all the hosts in the provided host sets. + ListHosts(context.Context, *ListHostsRequest) (*ListHostsResponse, error) + mustEmbedUnimplementedHostPluginServiceServer() +} + +// UnimplementedHostPluginServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHostPluginServiceServer struct { +} + +func (UnimplementedHostPluginServiceServer) OnCreateCatalog(context.Context, *OnCreateCatalogRequest) (*OnCreateCatalogResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnCreateCatalog not implemented") +} +func (UnimplementedHostPluginServiceServer) OnUpdateCatalog(context.Context, *OnUpdateCatalogRequest) (*OnUpdateCatalogResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnUpdateCatalog not implemented") +} +func (UnimplementedHostPluginServiceServer) OnDeleteCatalog(context.Context, *OnDeleteCatalogRequest) (*OnDeleteCatalogResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnDeleteCatalog not implemented") +} +func (UnimplementedHostPluginServiceServer) OnCreateSet(context.Context, *OnCreateSetRequest) (*OnCreateSetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnCreateSet not implemented") +} +func (UnimplementedHostPluginServiceServer) OnUpdateSet(context.Context, *OnUpdateSetRequest) (*OnUpdateSetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnUpdateSet not implemented") +} +func (UnimplementedHostPluginServiceServer) OnDeleteSet(context.Context, *OnDeleteSetRequest) (*OnDeleteSetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnDeleteSet not implemented") +} +func (UnimplementedHostPluginServiceServer) ListHosts(context.Context, *ListHostsRequest) (*ListHostsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListHosts not implemented") +} +func (UnimplementedHostPluginServiceServer) mustEmbedUnimplementedHostPluginServiceServer() {} + +// UnsafeHostPluginServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HostPluginServiceServer will +// result in compilation errors. +type UnsafeHostPluginServiceServer interface { + mustEmbedUnimplementedHostPluginServiceServer() +} + +func RegisterHostPluginServiceServer(s grpc.ServiceRegistrar, srv HostPluginServiceServer) { + s.RegisterService(&HostPluginService_ServiceDesc, srv) +} + +func _HostPluginService_OnCreateCatalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnCreateCatalogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnCreateCatalog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnCreateCatalog", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnCreateCatalog(ctx, req.(*OnCreateCatalogRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_OnUpdateCatalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnUpdateCatalogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnUpdateCatalog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnUpdateCatalog", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnUpdateCatalog(ctx, req.(*OnUpdateCatalogRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_OnDeleteCatalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnDeleteCatalogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnDeleteCatalog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnDeleteCatalog", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnDeleteCatalog(ctx, req.(*OnDeleteCatalogRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_OnCreateSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnCreateSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnCreateSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnCreateSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnCreateSet(ctx, req.(*OnCreateSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_OnUpdateSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnUpdateSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnUpdateSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnUpdateSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnUpdateSet(ctx, req.(*OnUpdateSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_OnDeleteSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OnDeleteSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).OnDeleteSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/OnDeleteSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).OnDeleteSet(ctx, req.(*OnDeleteSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HostPluginService_ListHosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListHostsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HostPluginServiceServer).ListHosts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugin.HostPluginService/ListHosts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HostPluginServiceServer).ListHosts(ctx, req.(*ListHostsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HostPluginService_ServiceDesc is the grpc.ServiceDesc for HostPluginService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HostPluginService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "plugin.HostPluginService", + HandlerType: (*HostPluginServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "OnCreateCatalog", + Handler: _HostPluginService_OnCreateCatalog_Handler, + }, + { + MethodName: "OnUpdateCatalog", + Handler: _HostPluginService_OnUpdateCatalog_Handler, + }, + { + MethodName: "OnDeleteCatalog", + Handler: _HostPluginService_OnDeleteCatalog_Handler, + }, + { + MethodName: "OnCreateSet", + Handler: _HostPluginService_OnCreateSet_Handler, + }, + { + MethodName: "OnUpdateSet", + Handler: _HostPluginService_OnUpdateSet_Handler, + }, + { + MethodName: "OnDeleteSet", + Handler: _HostPluginService_OnDeleteSet_Handler, + }, + { + MethodName: "ListHosts", + Handler: _HostPluginService_ListHosts_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "plugin/host_plugin_service.proto", +} From 2132040260edd1dfa7535f51d836e4bf14b90c3b Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Tue, 31 Aug 2021 16:57:06 -0700 Subject: [PATCH 02/71] Fix host_plugin_service.proto; run make gen --- internal/gen/testing/event/taggable.go | 1 - .../plugin/{ => v1}/host_plugin_service.proto | 2 +- plugin/proto/host_plugin_service.pb.go | 635 +++++++++--------- plugin/proto/host_plugin_service_grpc.pb.go | 32 +- 4 files changed, 337 insertions(+), 333 deletions(-) rename internal/proto/local/plugin/{ => v1}/host_plugin_service.proto (99%) diff --git a/internal/gen/testing/event/taggable.go b/internal/gen/testing/event/taggable.go index 1e2ea5bb2..cdc562822 100644 --- a/internal/gen/testing/event/taggable.go +++ b/internal/gen/testing/event/taggable.go @@ -21,7 +21,6 @@ func (req *TestAuthenticateRequest) Tags() ([]encrypt.PointerTag, error) { // Tags implements the encrypt.Taggable interface which allows // TestEventAuthenResponse Attributes to be classified for the encrypt filter. func (req *TestAuthenticateResponse) Tags() ([]encrypt.PointerTag, error) { - var tags []encrypt.PointerTag if req.Attributes != nil { diff --git a/internal/proto/local/plugin/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto similarity index 99% rename from internal/proto/local/plugin/host_plugin_service.proto rename to internal/proto/local/plugin/v1/host_plugin_service.proto index ffcc794d4..9e7ae8f1d 100644 --- a/internal/proto/local/plugin/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package plugin; +package plugin.v1; option go_package = "github.com/hashicorp/boundary/plugin/proto;proto"; diff --git a/plugin/proto/host_plugin_service.pb.go b/plugin/proto/host_plugin_service.pb.go index db6fa8fd2..f8f170018 100644 --- a/plugin/proto/host_plugin_service.pb.go +++ b/plugin/proto/host_plugin_service.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.27.1 // protoc v3.15.8 -// source: plugin/host_plugin_service.proto +// source: plugin/v1/host_plugin_service.proto package proto @@ -38,7 +38,7 @@ type OnCreateCatalogRequest struct { func (x *OnCreateCatalogRequest) Reset() { *x = OnCreateCatalogRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[0] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -51,7 +51,7 @@ func (x *OnCreateCatalogRequest) String() string { func (*OnCreateCatalogRequest) ProtoMessage() {} func (x *OnCreateCatalogRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[0] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -64,7 +64,7 @@ func (x *OnCreateCatalogRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnCreateCatalogRequest.ProtoReflect.Descriptor instead. func (*OnCreateCatalogRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{0} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{0} } func (x *OnCreateCatalogRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -89,7 +89,7 @@ type OnCreateCatalogResponse struct { func (x *OnCreateCatalogResponse) Reset() { *x = OnCreateCatalogResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[1] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -102,7 +102,7 @@ func (x *OnCreateCatalogResponse) String() string { func (*OnCreateCatalogResponse) ProtoMessage() {} func (x *OnCreateCatalogResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[1] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -115,7 +115,7 @@ func (x *OnCreateCatalogResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnCreateCatalogResponse.ProtoReflect.Descriptor instead. func (*OnCreateCatalogResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{1} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{1} } func (x *OnCreateCatalogResponse) GetPersisted() *HostCatalogPersisted { @@ -143,7 +143,7 @@ type OnUpdateCatalogRequest struct { func (x *OnUpdateCatalogRequest) Reset() { *x = OnUpdateCatalogRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[2] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -156,7 +156,7 @@ func (x *OnUpdateCatalogRequest) String() string { func (*OnUpdateCatalogRequest) ProtoMessage() {} func (x *OnUpdateCatalogRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[2] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -169,7 +169,7 @@ func (x *OnUpdateCatalogRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnUpdateCatalogRequest.ProtoReflect.Descriptor instead. func (*OnUpdateCatalogRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{2} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{2} } func (x *OnUpdateCatalogRequest) GetCurrentCatalog() *hostcatalogs.HostCatalog { @@ -209,7 +209,7 @@ type OnUpdateCatalogResponse struct { func (x *OnUpdateCatalogResponse) Reset() { *x = OnUpdateCatalogResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[3] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -222,7 +222,7 @@ func (x *OnUpdateCatalogResponse) String() string { func (*OnUpdateCatalogResponse) ProtoMessage() {} func (x *OnUpdateCatalogResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[3] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -235,7 +235,7 @@ func (x *OnUpdateCatalogResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnUpdateCatalogResponse.ProtoReflect.Descriptor instead. func (*OnUpdateCatalogResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{3} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{3} } func (x *OnUpdateCatalogResponse) GetPersisted() *HostCatalogPersisted { @@ -259,7 +259,7 @@ type OnDeleteCatalogRequest struct { func (x *OnDeleteCatalogRequest) Reset() { *x = OnDeleteCatalogRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[4] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -272,7 +272,7 @@ func (x *OnDeleteCatalogRequest) String() string { func (*OnDeleteCatalogRequest) ProtoMessage() {} func (x *OnDeleteCatalogRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[4] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -285,7 +285,7 @@ func (x *OnDeleteCatalogRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnDeleteCatalogRequest.ProtoReflect.Descriptor instead. func (*OnDeleteCatalogRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{4} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{4} } func (x *OnDeleteCatalogRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -311,7 +311,7 @@ type OnDeleteCatalogResponse struct { func (x *OnDeleteCatalogResponse) Reset() { *x = OnDeleteCatalogResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[5] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -324,7 +324,7 @@ func (x *OnDeleteCatalogResponse) String() string { func (*OnDeleteCatalogResponse) ProtoMessage() {} func (x *OnDeleteCatalogResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[5] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -337,7 +337,7 @@ func (x *OnDeleteCatalogResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnDeleteCatalogResponse.ProtoReflect.Descriptor instead. func (*OnDeleteCatalogResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{5} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{5} } type OnCreateSetRequest struct { @@ -356,7 +356,7 @@ type OnCreateSetRequest struct { func (x *OnCreateSetRequest) Reset() { *x = OnCreateSetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[6] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -369,7 +369,7 @@ func (x *OnCreateSetRequest) String() string { func (*OnCreateSetRequest) ProtoMessage() {} func (x *OnCreateSetRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[6] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -382,7 +382,7 @@ func (x *OnCreateSetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnCreateSetRequest.ProtoReflect.Descriptor instead. func (*OnCreateSetRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{6} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{6} } func (x *OnCreateSetRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -415,7 +415,7 @@ type OnCreateSetResponse struct { func (x *OnCreateSetResponse) Reset() { *x = OnCreateSetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[7] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -428,7 +428,7 @@ func (x *OnCreateSetResponse) String() string { func (*OnCreateSetResponse) ProtoMessage() {} func (x *OnCreateSetResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[7] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -441,7 +441,7 @@ func (x *OnCreateSetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnCreateSetResponse.ProtoReflect.Descriptor instead. func (*OnCreateSetResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{7} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{7} } type OnUpdateSetRequest struct { @@ -462,7 +462,7 @@ type OnUpdateSetRequest struct { func (x *OnUpdateSetRequest) Reset() { *x = OnUpdateSetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[8] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -475,7 +475,7 @@ func (x *OnUpdateSetRequest) String() string { func (*OnUpdateSetRequest) ProtoMessage() {} func (x *OnUpdateSetRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[8] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -488,7 +488,7 @@ func (x *OnUpdateSetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnUpdateSetRequest.ProtoReflect.Descriptor instead. func (*OnUpdateSetRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{8} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{8} } func (x *OnUpdateSetRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -528,7 +528,7 @@ type OnUpdateSetResponse struct { func (x *OnUpdateSetResponse) Reset() { *x = OnUpdateSetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[9] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -541,7 +541,7 @@ func (x *OnUpdateSetResponse) String() string { func (*OnUpdateSetResponse) ProtoMessage() {} func (x *OnUpdateSetResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[9] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -554,7 +554,7 @@ func (x *OnUpdateSetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnUpdateSetResponse.ProtoReflect.Descriptor instead. func (*OnUpdateSetResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{9} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{9} } type OnDeleteSetRequest struct { @@ -573,7 +573,7 @@ type OnDeleteSetRequest struct { func (x *OnDeleteSetRequest) Reset() { *x = OnDeleteSetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[10] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -586,7 +586,7 @@ func (x *OnDeleteSetRequest) String() string { func (*OnDeleteSetRequest) ProtoMessage() {} func (x *OnDeleteSetRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[10] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -599,7 +599,7 @@ func (x *OnDeleteSetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OnDeleteSetRequest.ProtoReflect.Descriptor instead. func (*OnDeleteSetRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{10} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{10} } func (x *OnDeleteSetRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -632,7 +632,7 @@ type OnDeleteSetResponse struct { func (x *OnDeleteSetResponse) Reset() { *x = OnDeleteSetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[11] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -645,7 +645,7 @@ func (x *OnDeleteSetResponse) String() string { func (*OnDeleteSetResponse) ProtoMessage() {} func (x *OnDeleteSetResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[11] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -658,7 +658,7 @@ func (x *OnDeleteSetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OnDeleteSetResponse.ProtoReflect.Descriptor instead. func (*OnDeleteSetResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{11} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{11} } type ListHostsRequest struct { @@ -678,7 +678,7 @@ type ListHostsRequest struct { func (x *ListHostsRequest) Reset() { *x = ListHostsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[12] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -691,7 +691,7 @@ func (x *ListHostsRequest) String() string { func (*ListHostsRequest) ProtoMessage() {} func (x *ListHostsRequest) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[12] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -704,7 +704,7 @@ func (x *ListHostsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHostsRequest.ProtoReflect.Descriptor instead. func (*ListHostsRequest) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{12} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{12} } func (x *ListHostsRequest) GetCatalog() *hostcatalogs.HostCatalog { @@ -740,7 +740,7 @@ type ListHostsResponse struct { func (x *ListHostsResponse) Reset() { *x = ListHostsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[13] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -753,7 +753,7 @@ func (x *ListHostsResponse) String() string { func (*ListHostsResponse) ProtoMessage() {} func (x *ListHostsResponse) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[13] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -766,7 +766,7 @@ func (x *ListHostsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHostsResponse.ProtoReflect.Descriptor instead. func (*ListHostsResponse) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{13} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{13} } func (x *ListHostsResponse) GetHosts() []*ListHostsResponseHost { @@ -797,7 +797,7 @@ type ListHostsResponseHost struct { func (x *ListHostsResponseHost) Reset() { *x = ListHostsResponseHost{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[14] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -810,7 +810,7 @@ func (x *ListHostsResponseHost) String() string { func (*ListHostsResponseHost) ProtoMessage() {} func (x *ListHostsResponseHost) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[14] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -823,7 +823,7 @@ func (x *ListHostsResponseHost) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHostsResponseHost.ProtoReflect.Descriptor instead. func (*ListHostsResponseHost) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{14} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{14} } func (x *ListHostsResponseHost) GetExternalId() string { @@ -868,7 +868,7 @@ type HostCatalogPersisted struct { func (x *HostCatalogPersisted) Reset() { *x = HostCatalogPersisted{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_host_plugin_service_proto_msgTypes[15] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -881,7 +881,7 @@ func (x *HostCatalogPersisted) String() string { func (*HostCatalogPersisted) ProtoMessage() {} func (x *HostCatalogPersisted) ProtoReflect() protoreflect.Message { - mi := &file_plugin_host_plugin_service_proto_msgTypes[15] + mi := &file_plugin_v1_host_plugin_service_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -894,7 +894,7 @@ func (x *HostCatalogPersisted) ProtoReflect() protoreflect.Message { // Deprecated: Use HostCatalogPersisted.ProtoReflect.Descriptor instead. func (*HostCatalogPersisted) Descriptor() ([]byte, []int) { - return file_plugin_host_plugin_service_proto_rawDescGZIP(), []int{15} + return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{15} } func (x *HostCatalogPersisted) GetData() *structpb.Struct { @@ -904,270 +904,275 @@ func (x *HostCatalogPersisted) GetData() *structpb.Struct { return nil } -var File_plugin_host_plugin_service_proto protoreflect.FileDescriptor +var File_plugin_v1_host_plugin_service_proto protoreflect.FileDescriptor -var file_plugin_host_plugin_service_proto_rawDesc = []byte{ - 0x0a, 0x20, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 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, 0x3b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, - 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x33, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, - 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x5f, 0x73, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, - 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x69, 0x0a, 0x16, 0x4f, 0x6e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, +var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 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, 0x3b, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x33, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x76, + 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, + 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x69, + 0x0a, 0x16, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x22, 0x58, 0x0a, 0x17, 0x4f, 0x6e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x22, 0x8f, 0x02, 0x0a, 0x16, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, + 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x0e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x56, + 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x22, 0x55, 0x0a, 0x17, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, - 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, - 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x8c, 0x02, 0x0a, 0x16, 0x4f, - 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x56, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, - 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, - 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x17, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, - 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, - 0x22, 0xa5, 0x01, 0x0a, 0x16, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, 0x09, - 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, - 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x4f, 0x6e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x58, 0x0a, 0x17, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, + 0xa8, 0x01, 0x0a, 0x16, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3f, 0x0a, 0x03, 0x73, - 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x09, - 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, - 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xb9, 0x02, 0x0a, 0x12, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x46, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x73, - 0x65, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x53, 0x65, 0x74, 0x12, - 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x28, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, - 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, - 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x09, 0x70, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, + 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x4f, 0x6e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3f, 0x0a, + 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, - 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, - 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, - 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe2, 0x01, - 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, - 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3d, + 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, + 0x13, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x02, 0x0a, 0x12, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, - 0x52, 0x04, 0x73, 0x65, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, - 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x64, 0x22, 0x48, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, - 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x8b, 0x01, 0x0a, - 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, - 0x64, 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, 0x0a, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, 0x32, - 0xa9, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x1e, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, - 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x46, 0x0a, 0x07, + 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x06, 0x6e, 0x65, + 0x77, 0x53, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf4, 0x01, 0x0a, 0x12, 0x4f, + 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, + 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, + 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, + 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, + 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, + 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x41, + 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x04, 0x73, 0x65, 0x74, + 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, + 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x8b, 0x01, + 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x64, 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, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, + 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, - 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, - 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4c, 0x69, 0x73, - 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, - 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, + 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, + 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( - file_plugin_host_plugin_service_proto_rawDescOnce sync.Once - file_plugin_host_plugin_service_proto_rawDescData = file_plugin_host_plugin_service_proto_rawDesc + file_plugin_v1_host_plugin_service_proto_rawDescOnce sync.Once + file_plugin_v1_host_plugin_service_proto_rawDescData = file_plugin_v1_host_plugin_service_proto_rawDesc ) -func file_plugin_host_plugin_service_proto_rawDescGZIP() []byte { - file_plugin_host_plugin_service_proto_rawDescOnce.Do(func() { - file_plugin_host_plugin_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_plugin_host_plugin_service_proto_rawDescData) +func file_plugin_v1_host_plugin_service_proto_rawDescGZIP() []byte { + file_plugin_v1_host_plugin_service_proto_rawDescOnce.Do(func() { + file_plugin_v1_host_plugin_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_plugin_v1_host_plugin_service_proto_rawDescData) }) - return file_plugin_host_plugin_service_proto_rawDescData + return file_plugin_v1_host_plugin_service_proto_rawDescData } -var file_plugin_host_plugin_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) -var file_plugin_host_plugin_service_proto_goTypes = []interface{}{ - (*OnCreateCatalogRequest)(nil), // 0: plugin.OnCreateCatalogRequest - (*OnCreateCatalogResponse)(nil), // 1: plugin.OnCreateCatalogResponse - (*OnUpdateCatalogRequest)(nil), // 2: plugin.OnUpdateCatalogRequest - (*OnUpdateCatalogResponse)(nil), // 3: plugin.OnUpdateCatalogResponse - (*OnDeleteCatalogRequest)(nil), // 4: plugin.OnDeleteCatalogRequest - (*OnDeleteCatalogResponse)(nil), // 5: plugin.OnDeleteCatalogResponse - (*OnCreateSetRequest)(nil), // 6: plugin.OnCreateSetRequest - (*OnCreateSetResponse)(nil), // 7: plugin.OnCreateSetResponse - (*OnUpdateSetRequest)(nil), // 8: plugin.OnUpdateSetRequest - (*OnUpdateSetResponse)(nil), // 9: plugin.OnUpdateSetResponse - (*OnDeleteSetRequest)(nil), // 10: plugin.OnDeleteSetRequest - (*OnDeleteSetResponse)(nil), // 11: plugin.OnDeleteSetResponse - (*ListHostsRequest)(nil), // 12: plugin.ListHostsRequest - (*ListHostsResponse)(nil), // 13: plugin.ListHostsResponse - (*ListHostsResponseHost)(nil), // 14: plugin.ListHostsResponseHost - (*HostCatalogPersisted)(nil), // 15: plugin.HostCatalogPersisted +var file_plugin_v1_host_plugin_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_plugin_v1_host_plugin_service_proto_goTypes = []interface{}{ + (*OnCreateCatalogRequest)(nil), // 0: plugin.v1.OnCreateCatalogRequest + (*OnCreateCatalogResponse)(nil), // 1: plugin.v1.OnCreateCatalogResponse + (*OnUpdateCatalogRequest)(nil), // 2: plugin.v1.OnUpdateCatalogRequest + (*OnUpdateCatalogResponse)(nil), // 3: plugin.v1.OnUpdateCatalogResponse + (*OnDeleteCatalogRequest)(nil), // 4: plugin.v1.OnDeleteCatalogRequest + (*OnDeleteCatalogResponse)(nil), // 5: plugin.v1.OnDeleteCatalogResponse + (*OnCreateSetRequest)(nil), // 6: plugin.v1.OnCreateSetRequest + (*OnCreateSetResponse)(nil), // 7: plugin.v1.OnCreateSetResponse + (*OnUpdateSetRequest)(nil), // 8: plugin.v1.OnUpdateSetRequest + (*OnUpdateSetResponse)(nil), // 9: plugin.v1.OnUpdateSetResponse + (*OnDeleteSetRequest)(nil), // 10: plugin.v1.OnDeleteSetRequest + (*OnDeleteSetResponse)(nil), // 11: plugin.v1.OnDeleteSetResponse + (*ListHostsRequest)(nil), // 12: plugin.v1.ListHostsRequest + (*ListHostsResponse)(nil), // 13: plugin.v1.ListHostsResponse + (*ListHostsResponseHost)(nil), // 14: plugin.v1.ListHostsResponseHost + (*HostCatalogPersisted)(nil), // 15: plugin.v1.HostCatalogPersisted (*hostcatalogs.HostCatalog)(nil), // 16: controller.api.resources.hostcatalogs.v1.HostCatalog (*hostsets.HostSet)(nil), // 17: controller.api.resources.hostsets.v1.HostSet (*structpb.Struct)(nil), // 18: google.protobuf.Struct } -var file_plugin_host_plugin_service_proto_depIdxs = []int32{ - 16, // 0: plugin.OnCreateCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 15, // 1: plugin.OnCreateCatalogResponse.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 2: plugin.OnUpdateCatalogRequest.current_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 16, // 3: plugin.OnUpdateCatalogRequest.new_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 15, // 4: plugin.OnUpdateCatalogRequest.persisted:type_name -> plugin.HostCatalogPersisted - 15, // 5: plugin.OnUpdateCatalogResponse.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 6: plugin.OnDeleteCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 15, // 7: plugin.OnDeleteCatalogRequest.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 8: plugin.OnCreateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 9: plugin.OnCreateSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 10: plugin.OnCreateSetRequest.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 11: plugin.OnUpdateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 12: plugin.OnUpdateSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet - 17, // 13: plugin.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 14: plugin.OnUpdateSetRequest.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 15: plugin.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 16: plugin.OnDeleteSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 17: plugin.OnDeleteSetRequest.persisted:type_name -> plugin.HostCatalogPersisted - 16, // 18: plugin.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 19: plugin.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 20: plugin.ListHostsRequest.persisted:type_name -> plugin.HostCatalogPersisted - 14, // 21: plugin.ListHostsResponse.hosts:type_name -> plugin.ListHostsResponseHost - 18, // 22: plugin.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct - 18, // 23: plugin.HostCatalogPersisted.data:type_name -> google.protobuf.Struct - 0, // 24: plugin.HostPluginService.OnCreateCatalog:input_type -> plugin.OnCreateCatalogRequest - 2, // 25: plugin.HostPluginService.OnUpdateCatalog:input_type -> plugin.OnUpdateCatalogRequest - 4, // 26: plugin.HostPluginService.OnDeleteCatalog:input_type -> plugin.OnDeleteCatalogRequest - 6, // 27: plugin.HostPluginService.OnCreateSet:input_type -> plugin.OnCreateSetRequest - 8, // 28: plugin.HostPluginService.OnUpdateSet:input_type -> plugin.OnUpdateSetRequest - 10, // 29: plugin.HostPluginService.OnDeleteSet:input_type -> plugin.OnDeleteSetRequest - 12, // 30: plugin.HostPluginService.ListHosts:input_type -> plugin.ListHostsRequest - 1, // 31: plugin.HostPluginService.OnCreateCatalog:output_type -> plugin.OnCreateCatalogResponse - 3, // 32: plugin.HostPluginService.OnUpdateCatalog:output_type -> plugin.OnUpdateCatalogResponse - 5, // 33: plugin.HostPluginService.OnDeleteCatalog:output_type -> plugin.OnDeleteCatalogResponse - 7, // 34: plugin.HostPluginService.OnCreateSet:output_type -> plugin.OnCreateSetResponse - 9, // 35: plugin.HostPluginService.OnUpdateSet:output_type -> plugin.OnUpdateSetResponse - 11, // 36: plugin.HostPluginService.OnDeleteSet:output_type -> plugin.OnDeleteSetResponse - 13, // 37: plugin.HostPluginService.ListHosts:output_type -> plugin.ListHostsResponse +var file_plugin_v1_host_plugin_service_proto_depIdxs = []int32{ + 16, // 0: plugin.v1.OnCreateCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 1: plugin.v1.OnCreateCatalogResponse.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 2: plugin.v1.OnUpdateCatalogRequest.current_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 16, // 3: plugin.v1.OnUpdateCatalogRequest.new_catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 4: plugin.v1.OnUpdateCatalogRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 15, // 5: plugin.v1.OnUpdateCatalogResponse.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 6: plugin.v1.OnDeleteCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 15, // 7: plugin.v1.OnDeleteCatalogRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 8: plugin.v1.OnCreateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 9: plugin.v1.OnCreateSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 10: plugin.v1.OnCreateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 11: plugin.v1.OnUpdateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 12: plugin.v1.OnUpdateSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 17, // 13: plugin.v1.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 14: plugin.v1.OnUpdateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 15: plugin.v1.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 16: plugin.v1.OnDeleteSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 17: plugin.v1.OnDeleteSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 18: plugin.v1.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 19: plugin.v1.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 20: plugin.v1.ListHostsRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 14, // 21: plugin.v1.ListHostsResponse.hosts:type_name -> plugin.v1.ListHostsResponseHost + 18, // 22: plugin.v1.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct + 18, // 23: plugin.v1.HostCatalogPersisted.data:type_name -> google.protobuf.Struct + 0, // 24: plugin.v1.HostPluginService.OnCreateCatalog:input_type -> plugin.v1.OnCreateCatalogRequest + 2, // 25: plugin.v1.HostPluginService.OnUpdateCatalog:input_type -> plugin.v1.OnUpdateCatalogRequest + 4, // 26: plugin.v1.HostPluginService.OnDeleteCatalog:input_type -> plugin.v1.OnDeleteCatalogRequest + 6, // 27: plugin.v1.HostPluginService.OnCreateSet:input_type -> plugin.v1.OnCreateSetRequest + 8, // 28: plugin.v1.HostPluginService.OnUpdateSet:input_type -> plugin.v1.OnUpdateSetRequest + 10, // 29: plugin.v1.HostPluginService.OnDeleteSet:input_type -> plugin.v1.OnDeleteSetRequest + 12, // 30: plugin.v1.HostPluginService.ListHosts:input_type -> plugin.v1.ListHostsRequest + 1, // 31: plugin.v1.HostPluginService.OnCreateCatalog:output_type -> plugin.v1.OnCreateCatalogResponse + 3, // 32: plugin.v1.HostPluginService.OnUpdateCatalog:output_type -> plugin.v1.OnUpdateCatalogResponse + 5, // 33: plugin.v1.HostPluginService.OnDeleteCatalog:output_type -> plugin.v1.OnDeleteCatalogResponse + 7, // 34: plugin.v1.HostPluginService.OnCreateSet:output_type -> plugin.v1.OnCreateSetResponse + 9, // 35: plugin.v1.HostPluginService.OnUpdateSet:output_type -> plugin.v1.OnUpdateSetResponse + 11, // 36: plugin.v1.HostPluginService.OnDeleteSet:output_type -> plugin.v1.OnDeleteSetResponse + 13, // 37: plugin.v1.HostPluginService.ListHosts:output_type -> plugin.v1.ListHostsResponse 31, // [31:38] is the sub-list for method output_type 24, // [24:31] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name @@ -1175,13 +1180,13 @@ var file_plugin_host_plugin_service_proto_depIdxs = []int32{ 0, // [0:24] is the sub-list for field type_name } -func init() { file_plugin_host_plugin_service_proto_init() } -func file_plugin_host_plugin_service_proto_init() { - if File_plugin_host_plugin_service_proto != nil { +func init() { file_plugin_v1_host_plugin_service_proto_init() } +func file_plugin_v1_host_plugin_service_proto_init() { + if File_plugin_v1_host_plugin_service_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_plugin_host_plugin_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnCreateCatalogRequest); i { case 0: return &v.state @@ -1193,7 +1198,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnCreateCatalogResponse); i { case 0: return &v.state @@ -1205,7 +1210,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnUpdateCatalogRequest); i { case 0: return &v.state @@ -1217,7 +1222,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnUpdateCatalogResponse); i { case 0: return &v.state @@ -1229,7 +1234,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnDeleteCatalogRequest); i { case 0: return &v.state @@ -1241,7 +1246,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnDeleteCatalogResponse); i { case 0: return &v.state @@ -1253,7 +1258,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnCreateSetRequest); i { case 0: return &v.state @@ -1265,7 +1270,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnCreateSetResponse); i { case 0: return &v.state @@ -1277,7 +1282,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnUpdateSetRequest); i { case 0: return &v.state @@ -1289,7 +1294,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnUpdateSetResponse); i { case 0: return &v.state @@ -1301,7 +1306,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnDeleteSetRequest); i { case 0: return &v.state @@ -1313,7 +1318,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OnDeleteSetResponse); i { case 0: return &v.state @@ -1325,7 +1330,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListHostsRequest); i { case 0: return &v.state @@ -1337,7 +1342,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListHostsResponse); i { case 0: return &v.state @@ -1349,7 +1354,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListHostsResponseHost); i { case 0: return &v.state @@ -1361,7 +1366,7 @@ func file_plugin_host_plugin_service_proto_init() { return nil } } - file_plugin_host_plugin_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_plugin_v1_host_plugin_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HostCatalogPersisted); i { case 0: return &v.state @@ -1378,18 +1383,18 @@ func file_plugin_host_plugin_service_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_plugin_host_plugin_service_proto_rawDesc, + RawDescriptor: file_plugin_v1_host_plugin_service_proto_rawDesc, NumEnums: 0, NumMessages: 16, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_plugin_host_plugin_service_proto_goTypes, - DependencyIndexes: file_plugin_host_plugin_service_proto_depIdxs, - MessageInfos: file_plugin_host_plugin_service_proto_msgTypes, + GoTypes: file_plugin_v1_host_plugin_service_proto_goTypes, + DependencyIndexes: file_plugin_v1_host_plugin_service_proto_depIdxs, + MessageInfos: file_plugin_v1_host_plugin_service_proto_msgTypes, }.Build() - File_plugin_host_plugin_service_proto = out.File - file_plugin_host_plugin_service_proto_rawDesc = nil - file_plugin_host_plugin_service_proto_goTypes = nil - file_plugin_host_plugin_service_proto_depIdxs = nil + File_plugin_v1_host_plugin_service_proto = out.File + file_plugin_v1_host_plugin_service_proto_rawDesc = nil + file_plugin_v1_host_plugin_service_proto_goTypes = nil + file_plugin_v1_host_plugin_service_proto_depIdxs = nil } diff --git a/plugin/proto/host_plugin_service_grpc.pb.go b/plugin/proto/host_plugin_service_grpc.pb.go index e6fe33224..bbfe8dfe3 100644 --- a/plugin/proto/host_plugin_service_grpc.pb.go +++ b/plugin/proto/host_plugin_service_grpc.pb.go @@ -47,7 +47,7 @@ func NewHostPluginServiceClient(cc grpc.ClientConnInterface) HostPluginServiceCl func (c *hostPluginServiceClient) OnCreateCatalog(ctx context.Context, in *OnCreateCatalogRequest, opts ...grpc.CallOption) (*OnCreateCatalogResponse, error) { out := new(OnCreateCatalogResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnCreateCatalog", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnCreateCatalog", in, out, opts...) if err != nil { return nil, err } @@ -56,7 +56,7 @@ func (c *hostPluginServiceClient) OnCreateCatalog(ctx context.Context, in *OnCre func (c *hostPluginServiceClient) OnUpdateCatalog(ctx context.Context, in *OnUpdateCatalogRequest, opts ...grpc.CallOption) (*OnUpdateCatalogResponse, error) { out := new(OnUpdateCatalogResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnUpdateCatalog", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnUpdateCatalog", in, out, opts...) if err != nil { return nil, err } @@ -65,7 +65,7 @@ func (c *hostPluginServiceClient) OnUpdateCatalog(ctx context.Context, in *OnUpd func (c *hostPluginServiceClient) OnDeleteCatalog(ctx context.Context, in *OnDeleteCatalogRequest, opts ...grpc.CallOption) (*OnDeleteCatalogResponse, error) { out := new(OnDeleteCatalogResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnDeleteCatalog", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnDeleteCatalog", in, out, opts...) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func (c *hostPluginServiceClient) OnDeleteCatalog(ctx context.Context, in *OnDel func (c *hostPluginServiceClient) OnCreateSet(ctx context.Context, in *OnCreateSetRequest, opts ...grpc.CallOption) (*OnCreateSetResponse, error) { out := new(OnCreateSetResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnCreateSet", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnCreateSet", in, out, opts...) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (c *hostPluginServiceClient) OnCreateSet(ctx context.Context, in *OnCreateS func (c *hostPluginServiceClient) OnUpdateSet(ctx context.Context, in *OnUpdateSetRequest, opts ...grpc.CallOption) (*OnUpdateSetResponse, error) { out := new(OnUpdateSetResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnUpdateSet", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnUpdateSet", in, out, opts...) if err != nil { return nil, err } @@ -92,7 +92,7 @@ func (c *hostPluginServiceClient) OnUpdateSet(ctx context.Context, in *OnUpdateS func (c *hostPluginServiceClient) OnDeleteSet(ctx context.Context, in *OnDeleteSetRequest, opts ...grpc.CallOption) (*OnDeleteSetResponse, error) { out := new(OnDeleteSetResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/OnDeleteSet", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/OnDeleteSet", in, out, opts...) if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (c *hostPluginServiceClient) OnDeleteSet(ctx context.Context, in *OnDeleteS func (c *hostPluginServiceClient) ListHosts(ctx context.Context, in *ListHostsRequest, opts ...grpc.CallOption) (*ListHostsResponse, error) { out := new(ListHostsResponse) - err := c.cc.Invoke(ctx, "/plugin.HostPluginService/ListHosts", in, out, opts...) + err := c.cc.Invoke(ctx, "/plugin.v1.HostPluginService/ListHosts", in, out, opts...) if err != nil { return nil, err } @@ -180,7 +180,7 @@ func _HostPluginService_OnCreateCatalog_Handler(srv interface{}, ctx context.Con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnCreateCatalog", + FullMethod: "/plugin.v1.HostPluginService/OnCreateCatalog", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnCreateCatalog(ctx, req.(*OnCreateCatalogRequest)) @@ -198,7 +198,7 @@ func _HostPluginService_OnUpdateCatalog_Handler(srv interface{}, ctx context.Con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnUpdateCatalog", + FullMethod: "/plugin.v1.HostPluginService/OnUpdateCatalog", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnUpdateCatalog(ctx, req.(*OnUpdateCatalogRequest)) @@ -216,7 +216,7 @@ func _HostPluginService_OnDeleteCatalog_Handler(srv interface{}, ctx context.Con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnDeleteCatalog", + FullMethod: "/plugin.v1.HostPluginService/OnDeleteCatalog", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnDeleteCatalog(ctx, req.(*OnDeleteCatalogRequest)) @@ -234,7 +234,7 @@ func _HostPluginService_OnCreateSet_Handler(srv interface{}, ctx context.Context } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnCreateSet", + FullMethod: "/plugin.v1.HostPluginService/OnCreateSet", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnCreateSet(ctx, req.(*OnCreateSetRequest)) @@ -252,7 +252,7 @@ func _HostPluginService_OnUpdateSet_Handler(srv interface{}, ctx context.Context } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnUpdateSet", + FullMethod: "/plugin.v1.HostPluginService/OnUpdateSet", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnUpdateSet(ctx, req.(*OnUpdateSetRequest)) @@ -270,7 +270,7 @@ func _HostPluginService_OnDeleteSet_Handler(srv interface{}, ctx context.Context } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/OnDeleteSet", + FullMethod: "/plugin.v1.HostPluginService/OnDeleteSet", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).OnDeleteSet(ctx, req.(*OnDeleteSetRequest)) @@ -288,7 +288,7 @@ func _HostPluginService_ListHosts_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/plugin.HostPluginService/ListHosts", + FullMethod: "/plugin.v1.HostPluginService/ListHosts", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HostPluginServiceServer).ListHosts(ctx, req.(*ListHostsRequest)) @@ -300,7 +300,7 @@ func _HostPluginService_ListHosts_Handler(srv interface{}, ctx context.Context, // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var HostPluginService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "plugin.HostPluginService", + ServiceName: "plugin.v1.HostPluginService", HandlerType: (*HostPluginServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -333,5 +333,5 @@ var HostPluginService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "plugin/host_plugin_service.proto", + Metadata: "plugin/v1/host_plugin_service.proto", } From ad8839e6dec7b63beea1bb93ec973402fe0d9513 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Tue, 7 Sep 2021 13:27:53 -0700 Subject: [PATCH 03/71] Move protobufs to sdk/pbs/plugin (#1506) --- .../proto/local/plugin/v1/host_plugin_service.proto | 2 +- .../proto => sdk/pbs/plugin}/host_plugin_service.pb.go | 10 +++++----- .../pbs/plugin}/host_plugin_service_grpc.pb.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename {plugin/proto => sdk/pbs/plugin}/host_plugin_service.pb.go (99%) rename {plugin/proto => sdk/pbs/plugin}/host_plugin_service_grpc.pb.go (99%) diff --git a/internal/proto/local/plugin/v1/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto index 9e7ae8f1d..b670e6794 100644 --- a/internal/proto/local/plugin/v1/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package plugin.v1; -option go_package = "github.com/hashicorp/boundary/plugin/proto;proto"; +option go_package = "github.com/hashicorp/boundary/sdk/pbs/plugin;plugin"; import "google/protobuf/struct.proto"; diff --git a/plugin/proto/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go similarity index 99% rename from plugin/proto/host_plugin_service.pb.go rename to sdk/pbs/plugin/host_plugin_service.pb.go index f8f170018..3a5e7cbc8 100644 --- a/plugin/proto/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -4,7 +4,7 @@ // protoc v3.15.8 // source: plugin/v1/host_plugin_service.proto -package proto +package plugin import ( hostcatalogs "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" @@ -1093,11 +1093,11 @@ var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/plugin/proto/host_plugin_service_grpc.pb.go b/sdk/pbs/plugin/host_plugin_service_grpc.pb.go similarity index 99% rename from plugin/proto/host_plugin_service_grpc.pb.go rename to sdk/pbs/plugin/host_plugin_service_grpc.pb.go index bbfe8dfe3..0819b6df4 100644 --- a/plugin/proto/host_plugin_service_grpc.pb.go +++ b/sdk/pbs/plugin/host_plugin_service_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -package proto +package plugin import ( context "context" From 71cba858e3f7e65d9baed36ebb0841b4069614be Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 8 Sep 2021 12:41:57 -0400 Subject: [PATCH 04/71] Add secrets to host catalogs API struct (#1512) This also changes the address from a plugin from a scalar value to other fields, starting with an IP address array. --- api/hostcatalogs/host_catalog.gen.go | 1 + api/hostcatalogs/option.gen.go | 6 + internal/api/genapi/input.go | 6 + internal/gen/controller.swagger.json | 4 + .../hostcatalogs/v1/host_catalog.proto | 3 + .../local/plugin/v1/host_plugin_service.proto | 27 ++-- .../resources/hostcatalogs/host_catalog.pb.go | 84 +++++++----- sdk/pbs/plugin/host_plugin_service.pb.go | 124 +++++++++--------- 8 files changed, 144 insertions(+), 111 deletions(-) diff --git a/api/hostcatalogs/host_catalog.gen.go b/api/hostcatalogs/host_catalog.gen.go index ffa8f3856..abce123ff 100644 --- a/api/hostcatalogs/host_catalog.gen.go +++ b/api/hostcatalogs/host_catalog.gen.go @@ -23,6 +23,7 @@ type HostCatalog struct { Version uint32 `json:"version,omitempty"` Type string `json:"type,omitempty"` Attributes map[string]interface{} `json:"attributes,omitempty"` + Secrets map[string]interface{} `json:"secrets,omitempty"` AuthorizedActions []string `json:"authorized_actions,omitempty"` AuthorizedCollectionActions map[string][]string `json:"authorized_collection_actions,omitempty"` diff --git a/api/hostcatalogs/option.gen.go b/api/hostcatalogs/option.gen.go index f968ede87..f0d29cee6 100644 --- a/api/hostcatalogs/option.gen.go +++ b/api/hostcatalogs/option.gen.go @@ -122,3 +122,9 @@ func DefaultName() Option { o.postMap["name"] = nil } } + +func WithSecrets(inSecrets map[string]interface{}) Option { + return func(o *options) { + o.postMap["secrets"] = inSecrets + } +} diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 0a3cba000..9715484fc 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -502,6 +502,12 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, + fieldOverrides: []fieldInfo{ + { + Name: "Secrets", + SkipDefault: true, + }, + }, pluralResourceName: "host-catalogs", typeOnCreate: true, versionEnabled: true, diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index e373d1b2c..ba95cd61e 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -3714,6 +3714,10 @@ "type": "object", "description": "Attributes specific to the catalog type." }, + "secrets": { + "type": "object", + "description": "Secrets specific to the catalog type. These are never output." + }, "authorized_actions": { "type": "array", "items": { diff --git a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto index 22f1c5d9f..80faeae35 100644 --- a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto +++ b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto @@ -43,6 +43,9 @@ message HostCatalog { // Attributes specific to the catalog type. google.protobuf.Struct attributes = 100 [(custom_options.v1.generate_sdk_option) = true]; + // Secrets specific to the catalog type. These are never output. + google.protobuf.Struct secrets = 110 [(custom_options.v1.generate_sdk_option) = true]; + // Output only. The available actions on this resource for this user. repeated string authorized_actions = 300 [json_name="authorized_actions"]; diff --git a/internal/proto/local/plugin/v1/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto index b670e6794..bef0ebbf4 100644 --- a/internal/proto/local/plugin/v1/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -45,10 +45,10 @@ message OnCreateCatalogRequest { } message OnCreateCatalogResponse { - // Secret data to persist encrypted within Boundary. This should be - // used to store authentication data and other necessary - // configuration to be used in later hooks and calls. Returning an - // error from the call will cause this data to not be persisted. + // Secret data to persist encrypted within Boundary. This should be used to + // store authentication data and other necessary configuration to be used in + // later hooks and calls. Returning an error from the call will cause this + // data to not be persisted. If this is nil, nothing is written. HostCatalogPersisted persisted = 10; } @@ -56,9 +56,9 @@ message OnUpdateCatalogRequest { // The existing state of the catalog. controller.api.resources.hostcatalogs.v1.HostCatalog current_catalog = 10; - // The requested new state of the catalog. This field may contain - // optional secret data that may have been updated from old - // authentication data contained within the persisted state. + // The requested new state of the catalog. This field may contain optional + // secret data that may have been updated from old authentication data + // contained within the persisted state. controller.api.resources.hostcatalogs.v1.HostCatalog new_catalog = 20; // The existing persisted secret data. @@ -66,11 +66,10 @@ message OnUpdateCatalogRequest { } message OnUpdateCatalogResponse { - // The updated secret data to persist encrypted within Boundary. - // It's important that this be returned if it existed previously, - // as the returned data overwrites the previously existing copy. If - // an error is returned, the update of the persisted data is - // aborted. + // The updated secret data to persist encrypted within Boundary. If an error + // is returned, the update of the persisted data is aborted. If this is nil, + // no changes are written. To remove all values, simply return an allocated + // but empty map. HostCatalogPersisted persisted = 10; } @@ -150,8 +149,8 @@ message ListHostsResponseHost { // useful, ie: a compute instance ID. string external_id = 10; - // Required. The primary address for the host. - string address = 20; + // Required. Any and all known IP addresses for the host. + repeated string ip_addresses = 20; // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network diff --git a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go index ee9200a02..706f73581 100644 --- a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go +++ b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go @@ -52,6 +52,8 @@ type HostCatalog struct { Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty"` // Attributes specific to the catalog type. Attributes *structpb.Struct `protobuf:"bytes,100,opt,name=attributes,proto3" json:"attributes,omitempty"` + // Secrets specific to the catalog type. These are never output. + Secrets *structpb.Struct `protobuf:"bytes,110,opt,name=secrets,proto3" json:"secrets,omitempty"` // Output only. The available actions on this resource for this user. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` // Output only. The authorized actions for the scope's collections. @@ -160,6 +162,13 @@ func (x *HostCatalog) GetAttributes() *structpb.Struct { return nil } +func (x *HostCatalog) GetSecrets() *structpb.Struct { + if x != nil { + return x.Secrets + } + return nil +} + func (x *HostCatalog) GetAuthorizedActions() []string { if x != nil { return x.AuthorizedActions @@ -195,7 +204,7 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_rawDesc = [ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xd4, 0x06, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x74, 0x6f, 0x22, 0x8d, 0x07, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, @@ -228,33 +237,37 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_rawDesc = [ 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, - 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9d, 0x01, - 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1d, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6a, 0x0a, - 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, - 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x56, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6a, 0x0a, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -286,13 +299,14 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_depIdxs = [ 4, // 3: controller.api.resources.hostcatalogs.v1.HostCatalog.created_time:type_name -> google.protobuf.Timestamp 4, // 4: controller.api.resources.hostcatalogs.v1.HostCatalog.updated_time:type_name -> google.protobuf.Timestamp 5, // 5: controller.api.resources.hostcatalogs.v1.HostCatalog.attributes:type_name -> google.protobuf.Struct - 1, // 6: controller.api.resources.hostcatalogs.v1.HostCatalog.authorized_collection_actions:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry - 6, // 7: controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry.value:type_name -> google.protobuf.ListValue - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 5, // 6: controller.api.resources.hostcatalogs.v1.HostCatalog.secrets:type_name -> google.protobuf.Struct + 1, // 7: controller.api.resources.hostcatalogs.v1.HostCatalog.authorized_collection_actions:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry + 6, // 8: controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry.value:type_name -> google.protobuf.ListValue + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_init() } diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index 3a5e7cbc8..40c4aed5c 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -79,10 +79,10 @@ type OnCreateCatalogResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Secret data to persist encrypted within Boundary. This should be - // used to store authentication data and other necessary - // configuration to be used in later hooks and calls. Returning an - // error from the call will cause this data to not be persisted. + // Secret data to persist encrypted within Boundary. This should be used to + // store authentication data and other necessary configuration to be used in + // later hooks and calls. Returning an error from the call will cause this + // data to not be persisted. If this is nil, no changes are written. Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` } @@ -202,7 +202,7 @@ type OnUpdateCatalogResponse struct { // It's important that this be returned if it existed previously, // as the returned data overwrites the previously existing copy. If // an error is returned, the update of the persisted data is - // aborted. + // aborted. If this is nil, no changes are written. Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` } @@ -786,8 +786,8 @@ type ListHostsResponseHost struct { // included in audit logs. It should be set to something unique and // useful, ie: a compute instance ID. ExternalId string `protobuf:"bytes,10,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` - // Required. The primary address for the host. - Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty"` + // Required. Any and all known IP addresses for the host. + IpAddresses []string `protobuf:"bytes,20,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network // addresses, etc. @@ -833,11 +833,11 @@ func (x *ListHostsResponseHost) GetExternalId() string { return "" } -func (x *ListHostsResponseHost) GetAddress() string { +func (x *ListHostsResponseHost) GetIpAddresses() []string { if x != nil { - return x.Address + return x.IpAddresses } - return "" + return nil } func (x *ListHostsResponseHost) GetAttributes() *structpb.Struct { @@ -1042,62 +1042,62 @@ var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x8b, 0x01, + 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x18, 0x64, 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, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, - 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, + 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, + 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, + 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, - 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, - 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, + 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, + 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From db9e1106a9dfb4aaccc4ff46d27b685b765b1e59 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 8 Sep 2021 10:06:38 -0700 Subject: [PATCH 05/71] feat(host): Add host plugin related schemas and tests (#1475) * feat(host): Add host plugin related schemas and tests This adds the schema files for host plugins, plugin based catalogs and plugin based sets. This also adds the protos and tests which utilize gorm to ensure the schemas enforce the constraints we expect. Along with this change was a migration which moved catalog names to the base type table so we can ensure all catalogs in a single scope, regardless of subtype, have a unique name. * Updated plugin schemas to do the following: - Add plugin_executable table - plugin table now also has scope_id and name w/ uniqueness constraints - host_plugin no longer has id_prefix column - host_plugin separates name and plugin_name - plugin_version table renamed version to symantic_version column. * Fix typo in delete_plugin_subtype trigger. Add update test for host plugins. * Update id prefixes. Fix public_id typos in protos and plugin_executable name in sql diagrams. * Created internal/plugin and moved base plugin structs to it and moved host plugin subdirectory to it. * Created internal/plugin and moved base plugin structs to it and moved host plugin subdirectory to it. * Add first pass at enums for OS and Arch supported by plugin executables. * Capitalizing the const enums for OS and Architectures. * Add comments to sql files. * Reviewer comments. Remove unsupported OSes and Archs from SQL and go constants. Add missing tests. * Be more permissive in the values the db accepts. The go logic can restrict which values it will allow getting populated into the db. * Removing constraints on which values can be placed inside operating_system and architecture columns. * Updating test helper functions to only create a single catalog or set. * Move files to new migration version. * Fixing job/plugin test. * Update table names, named a number of constraints, remove plugin version and executable tables. * Add constraints for plugin name and id prefix. * Rename constraint, remove unhelpful comment block. * Adding semantic version to plugin_host and set attributes to not null. * Remove semantic versioning from all locations. * Remove semantic_version from plugin_host table comment. * Fix op copy/paste error. Don't use deprecated errors methods. Update constraint names. Add docs. * Apply suggestions from code review Fixing constraint names. Co-authored-by: Michael Gaffney --- Makefile | 3 + .../migrations/postgres/16/01_plugin.up.sql | 78 +++ .../postgres/16/02_hostplugin.up.sql | 89 +++ .../migrations/postgres/16/03_host.up.sql | 61 ++ .../postgres/16/04_plugin_host.up.sql | 167 ++++++ internal/db/schema/postgres_migration.gen.go | 389 +++++++++++- internal/host/plugin/host_catalog.go | 66 ++ internal/host/plugin/host_catalog_secret.go | 92 +++ .../host/plugin/host_catalog_secret_test.go | 163 +++++ internal/host/plugin/host_catalog_test.go | 326 ++++++++++ internal/host/plugin/host_set.go | 53 ++ internal/host/plugin/host_set_test.go | 201 +++++++ internal/host/plugin/ids.go | 41 ++ internal/host/plugin/immutable_fields_test.go | 163 +++++ internal/host/plugin/options.go | 59 ++ internal/host/plugin/options_test.go | 29 + internal/host/plugin/query.go | 19 + internal/host/plugin/store/host.pb.go | 566 ++++++++++++++++++ internal/host/plugin/testing.go | 50 ++ internal/host/plugin/testing_test.go | 48 ++ internal/plugin/doc.go | 3 + internal/plugin/host/doc.go | 4 + internal/plugin/host/ids.go | 19 + internal/plugin/host/immutable_fields_test.go | 85 +++ internal/plugin/host/options.go | 50 ++ internal/plugin/host/options_test.go | 29 + internal/plugin/host/plugin.go | 43 ++ internal/plugin/host/plugin_test.go | 275 +++++++++ internal/plugin/host/store/plugin.pb.go | 267 +++++++++ internal/plugin/host/testing.go | 22 + internal/plugin/host/testing_test.go | 18 + internal/plugin/immutable_fields_test.go | 72 +++ internal/plugin/store/plugin.pb.go | 182 ++++++ internal/plugin/testing.go | 55 ++ internal/plugin/testing_test.go | 18 + .../storage/host/plugin/store/v1/host.proto | 112 ++++ .../storage/plugin/host/store/v1/plugin.proto | 45 ++ .../storage/plugin/store/v1/plugin.proto | 23 + .../job/additional_verification_test.go | 6 +- 39 files changed, 3987 insertions(+), 4 deletions(-) create mode 100644 internal/db/schema/migrations/postgres/16/01_plugin.up.sql create mode 100644 internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql create mode 100644 internal/db/schema/migrations/postgres/16/03_host.up.sql create mode 100644 internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql create mode 100644 internal/host/plugin/host_catalog.go create mode 100644 internal/host/plugin/host_catalog_secret.go create mode 100644 internal/host/plugin/host_catalog_secret_test.go create mode 100644 internal/host/plugin/host_catalog_test.go create mode 100644 internal/host/plugin/host_set.go create mode 100644 internal/host/plugin/host_set_test.go create mode 100644 internal/host/plugin/ids.go create mode 100644 internal/host/plugin/immutable_fields_test.go create mode 100644 internal/host/plugin/options.go create mode 100644 internal/host/plugin/options_test.go create mode 100644 internal/host/plugin/query.go create mode 100644 internal/host/plugin/store/host.pb.go create mode 100644 internal/host/plugin/testing.go create mode 100644 internal/host/plugin/testing_test.go create mode 100644 internal/plugin/doc.go create mode 100644 internal/plugin/host/doc.go create mode 100644 internal/plugin/host/ids.go create mode 100644 internal/plugin/host/immutable_fields_test.go create mode 100644 internal/plugin/host/options.go create mode 100644 internal/plugin/host/options_test.go create mode 100644 internal/plugin/host/plugin.go create mode 100644 internal/plugin/host/plugin_test.go create mode 100644 internal/plugin/host/store/plugin.pb.go create mode 100644 internal/plugin/host/testing.go create mode 100644 internal/plugin/host/testing_test.go create mode 100644 internal/plugin/immutable_fields_test.go create mode 100644 internal/plugin/store/plugin.pb.go create mode 100644 internal/plugin/testing.go create mode 100644 internal/plugin/testing_test.go create mode 100644 internal/proto/local/controller/storage/host/plugin/store/v1/host.proto create mode 100644 internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto create mode 100644 internal/proto/local/controller/storage/plugin/store/v1/plugin.proto diff --git a/Makefile b/Makefile index b0bc24aef..75dfd98ef 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,9 @@ protobuild: @protoc-go-inject-tag -input=./internal/db/db_test/db_test.pb.go @protoc-go-inject-tag -input=./internal/host/store/host.pb.go @protoc-go-inject-tag -input=./internal/host/static/store/static.pb.go + @protoc-go-inject-tag -input=./internal/host/plugin/store/host.pb.go + @protoc-go-inject-tag -input=./internal/plugin/host/store/plugin.pb.go + @protoc-go-inject-tag -input=./internal/plugin/store/plugin.pb.go @protoc-go-inject-tag -input=./internal/authtoken/store/authtoken.pb.go @protoc-go-inject-tag -input=./internal/auth/store/account.pb.go @protoc-go-inject-tag -input=./internal/auth/password/store/password.pb.go diff --git a/internal/db/schema/migrations/postgres/16/01_plugin.up.sql b/internal/db/schema/migrations/postgres/16/01_plugin.up.sql new file mode 100644 index 000000000..9ac669031 --- /dev/null +++ b/internal/db/schema/migrations/postgres/16/01_plugin.up.sql @@ -0,0 +1,78 @@ +begin; + + -- We are updating the plugin table here to have a scope id, since + -- subtypes of plugin should be scoped. We use 'global' as the default + -- just for the purpose of the migration since the scope cannot be null + -- and the one plugin that already exists (pi_system) can be globally + -- scoped. The immediately following statement removes this default. + -- We add a name so we can enforce that all plugins have a unique name + -- in a specific scope across all plugin subtypes. + alter table plugin + add column name wt_name, + add column scope_id wt_scope_id + not null + default 'global' + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade; + + alter table plugin + alter column scope_id drop default; + + -- Add constraints that enforce names are unique across all plugin subtypes + -- in a specific scope. + alter table plugin + add constraint plugin_scope_id_public_id_uq + unique (scope_id, public_id), + add constraint plugin_scope_id_name_uq + unique (scope_id, name); + + + -- insert, update, and delete plugin_subtypes are created since we are adding + -- subtyped plugins and we need to keep the base table plugin in sync with all + -- subtype tables. + create function insert_plugin_subtype() + returns trigger + as $$ + begin + insert into plugin + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_plugin_subtype() is + 'insert_plugin_subtype() inserts sub type name into the base type plugin table'; + + create function update_plugin_subtype() + returns trigger + as $$ + begin + update plugin set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_plugin_subtype() is + 'update_plugin_subtype() will update base plugin type name column with new values from sub type'; + + -- delete_plugin_subtype() is an after delete trigger function + -- for subtypes of plugin + create function delete_plugin_subtype() + returns trigger + as $$ + begin + delete from plugin + where public_id = old.public_id; + return null; -- result is ignored since this is an after trigger + end; + $$ language plpgsql; + comment on function delete_plugin_subtype is + 'delete_plugin_subtype() is an after trigger function for subytypes of plugin'; + + insert into oplog_ticket (name, version) + values + ('plugin', 1); + +commit; diff --git a/internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql b/internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql new file mode 100644 index 000000000..dd3f59077 --- /dev/null +++ b/internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql @@ -0,0 +1,89 @@ +begin; + +/* + ┌──────────────────┐ + │ plugin │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + └──────────────────┘ + ┼ + ┼ + │ + ┼ + ┼ + ┌──────────────────┐ + │ plugin_host │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + │name │ + │description │ + │version │ + │plugin_name │ + │id_prefix │ + └──────────────────┘ +*/ + create table plugin_host ( + public_id wt_plugin_id primary key, + scope_id wt_scope_id not null + -- TODO: Allow plugins to be created in different scopes and + -- constrain the host-catalog's plugin reference accordingly. + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + plugin_name text not null + constraint plugin_name_must_be_not_empty + check(length(trim(plugin_name)) > 0) + constraint plugin_name_must_be_lowercase + check(lower(trim(plugin_name)) = plugin_name) + constraint plugin_host_plugin_name_uq + unique, + id_prefix text not null + constraint plugin_id_prefix_must_be_not_empty + check(length(trim(id_prefix)) > 0) + constraint plugin_id_prefix_must_fit_format + check (id_prefix ~ '^[a-z0-9]*$') + constraint plugin_host_id_prefix_uq + unique, + constraint plugin_fkey + foreign key (scope_id, public_id) + references plugin(scope_id, public_id) + on delete cascade + on update cascade, + constraint plugin_host_scope_id_name_uq + unique(scope_id, name) + ); + + create trigger update_version_column after update on plugin_host + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on plugin_host + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on plugin_host + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on plugin_host + for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); + + create trigger insert_plugin_subtype before insert on plugin_host + for each row execute procedure insert_plugin_subtype(); + + create trigger update_plugin_subtype before update on plugin_host + for each row execute procedure update_plugin_subtype(); + + create trigger delete_plugin_subtype after delete on plugin_host + for each row execute procedure delete_plugin_subtype(); + + insert into oplog_ticket (name, version) + values + ('plugin_host', 1); + +commit; diff --git a/internal/db/schema/migrations/postgres/16/03_host.up.sql b/internal/db/schema/migrations/postgres/16/03_host.up.sql new file mode 100644 index 000000000..2597f9700 --- /dev/null +++ b/internal/db/schema/migrations/postgres/16/03_host.up.sql @@ -0,0 +1,61 @@ +begin; + + -- We are adding the name to the base host catalog type. This allows the db + -- to ensure that catalog names are unique in a scope across all subtypes. + alter table host_catalog + add column name wt_name; + + alter table host_catalog + add constraint host_catalog_scope_id_name_uq + unique (scope_id, name); + + -- Now that we've added the name column to the base type, we copy + -- the name from the static host catalog table into the base table. + update host_catalog + set name = st.name + from + static_host_catalog st + where + host_catalog.public_id = st.public_id and + st.name is not null; + + -- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql + -- to include the name. + -- insert_host_catalog_subtype() is a before insert trigger + -- function for subtypes of host_catalog + create or replace function insert_host_catalog_subtype() + returns trigger + as $$ + begin + insert into host_catalog + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_host_catalog_subtype() is + 'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table'; + + -- Now that we are tracking the name, which is mutable, we need to also + -- update the base table when the subtype tables are updated. + -- update_host_catalog_subtype() is intended to be used as a before update + -- trigger for all host catalog sub types. The purpose is to ensure that the + -- base table for host catalog to contain the updated names for each host catalog + -- in order to enforce uniqueness across all host catalogs, regardless of subtype, + -- in a given scope. + create function update_host_catalog_subtype() + returns trigger + as $$ + begin + update host_catalog set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_host_catalog_subtype() is + 'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type'; + + create trigger update_host_catalog_subtype before update on static_host_catalog + for each row execute procedure update_host_catalog_subtype(); + +commit; diff --git a/internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql b/internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql new file mode 100644 index 000000000..3b4323de7 --- /dev/null +++ b/internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql @@ -0,0 +1,167 @@ +begin; + +/* + ┌──────────────────┐ + │ plugin_host │ + ├──────────────────┤ + │public_id (pk) │ + │... │ + └──────────────────┘ + ┼ + ┼ + ○ + ╱│╲ + ┌─────────────────────┐ + │ host_plugin_catalog │ + ├─────────────────────┤ ┌───────────────────────────┐ + ┌────────────────┐ │public_id (pk) │ │host_plugin_catalog_secret │ + │host_catalog │ │plugin_id (fk) │ ├───────────────────────────┤ + ├────────────────┤ │scope_id (fk) │ │host_catalog_id (pk, fk) │ + │public_id │┼┼──○┼│name │┼┼──○┼│secret │ + │scope_id │ │description │ │key_id (fk) │ + └────────────────┘ │attributes │ └───────────────────────────┘ + ┼ └─────────────────────┘ + ┼ ┼ + │ ┼ + │ ○ + ○ ╱│╲ + ╱│╲ ┌────────────────────┐ + ┌────────────────┐ │ host_plugin_set │ + │ host_set │ ├────────────────────┤ + ├────────────────┤ │public_id (pk) │ + │public_id │ │host_catalog_id (fk)│ + │host_catalog_id │┼┼──○┼│name │ + └────────────────┘ │description │ + │attributes │ + └────────────────────┘ + +*/ + + create table host_plugin_catalog ( + public_id wt_public_id primary key, + scope_id wt_scope_id not null + constraint iam_scope_fkey + references iam_scope (public_id) + on delete cascade + on update cascade, + plugin_id wt_plugin_id not null + constraint plugin_host_fkey + references plugin_host (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + attributes bytea not null + constraint attributes_must_not_be_empty + check(length(attributes) > 0), + constraint host_catalog_fkey + foreign key (scope_id, public_id) + references host_catalog (scope_id, public_id) + on delete cascade + on update cascade, + constraint host_plugin_catalog_scope_id_name_uq + unique(scope_id, name) + ); + + create trigger update_version_column after update on host_plugin_catalog + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_catalog + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_catalog + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_catalog + for each row execute procedure immutable_columns('public_id', 'scope_id', 'plugin_id', 'create_time'); + + create trigger insert_host_catalog_subtype before insert on host_plugin_catalog + for each row execute procedure insert_host_catalog_subtype(); + + create trigger update_host_catalog_subtype before update on host_plugin_catalog + for each row execute procedure update_host_catalog_subtype(); + + create trigger delete_host_catalog_subtype after delete on host_plugin_catalog + for each row execute procedure delete_host_catalog_subtype(); + + create table host_plugin_catalog_secret ( + catalog_id wt_public_id primary key + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + create_time wt_timestamp, + update_time wt_timestamp, + secret bytea not null -- encrypted value + constraint secret_must_not_be_empty + check(length(secret) > 0), + key_id wt_private_id not null + constraint kms_database_key_version_fkey + references kms_database_key_version (private_id) + on delete restrict + on update cascade + ); + + create trigger update_time_column before update on host_plugin_catalog_secret + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_catalog_secret + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_catalog_secret + for each row execute procedure immutable_columns('catalog_id', 'create_time'); + + create table host_plugin_set ( + public_id wt_public_id primary key, + catalog_id wt_public_id not null + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + attributes bytea not null + constraint attributes_must_not_be_empty + check(length(attributes) > 0), + constraint host_plugin_set_catalog_id_name_uq + unique(catalog_id, name), + constraint host_set_fkey + foreign key (catalog_id, public_id) + references host_set (catalog_id, public_id) + on delete cascade + on update cascade, + constraint host_plugin_set_catalog_id_public_id_uq + unique(catalog_id, public_id) + ); + + create trigger update_version_column after update on host_plugin_set + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_set + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_set + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_set + for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + + create trigger insert_host_set_subtype before insert on host_plugin_set + for each row execute procedure insert_host_set_subtype(); + + create trigger delete_host_set_subtype after delete on host_plugin_set + for each row execute procedure delete_host_set_subtype(); + + insert into oplog_ticket (name, version) + values + ('host_plugin_catalog', 1), + ('host_plugin_catalog_secret', 1), + ('host_plugin_set', 1); + +commit; diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 852676e63..d6373fb49 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -4,7 +4,7 @@ package schema func init() { migrationStates["postgres"] = migrationState{ - binarySchemaVersion: 15002, + binarySchemaVersion: 16004, upMigrations: map[int][]byte{ 1: []byte(` create domain wt_public_id as text @@ -6938,6 +6938,393 @@ alter table wh_host_dimension end; $$ language plpgsql; +`), + 16001: []byte(` +-- We are updating the plugin table here to have a scope id, since + -- subtypes of plugin should be scoped. We use 'global' as the default + -- just for the purpose of the migration since the scope cannot be null + -- and the one plugin that already exists (pi_system) can be globally + -- scoped. The immediately following statement removes this default. + -- We add a name so we can enforce that all plugins have a unique name + -- in a specific scope across all plugin subtypes. + alter table plugin + add column name wt_name, + add column scope_id wt_scope_id + not null + default 'global' + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade; + + alter table plugin + alter column scope_id drop default; + + -- Add constraints that enforce names are unique across all plugin subtypes + -- in a specific scope. + alter table plugin + add constraint plugin_scope_id_public_id_uq + unique (scope_id, public_id), + add constraint plugin_scope_id_name_uq + unique (scope_id, name); + + + -- insert, update, and delete plugin_subtypes are created since we are adding + -- subtyped plugins and we need to keep the base table plugin in sync with all + -- subtype tables. + create function insert_plugin_subtype() + returns trigger + as $$ + begin + insert into plugin + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_plugin_subtype() is + 'insert_plugin_subtype() inserts sub type name into the base type plugin table'; + + create function update_plugin_subtype() + returns trigger + as $$ + begin + update plugin set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_plugin_subtype() is + 'update_plugin_subtype() will update base plugin type name column with new values from sub type'; + + -- delete_plugin_subtype() is an after delete trigger function + -- for subtypes of plugin + create function delete_plugin_subtype() + returns trigger + as $$ + begin + delete from plugin + where public_id = old.public_id; + return null; -- result is ignored since this is an after trigger + end; + $$ language plpgsql; + comment on function delete_plugin_subtype is + 'delete_plugin_subtype() is an after trigger function for subytypes of plugin'; + + insert into oplog_ticket (name, version) + values + ('plugin', 1); +`), + 16002: []byte(` +/* + ┌──────────────────┐ + │ plugin │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + └──────────────────┘ + ┼ + ┼ + │ + ┼ + ┼ + ┌──────────────────┐ + │ plugin_host │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + │name │ + │description │ + │version │ + │plugin_name │ + │id_prefix │ + └──────────────────┘ +*/ + create table plugin_host ( + public_id wt_plugin_id primary key, + scope_id wt_scope_id not null + -- TODO: Allow plugins to be created in different scopes and + -- constrain the host-catalog's plugin reference accordingly. + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + plugin_name text not null + constraint plugin_name_must_be_not_empty + check(length(trim(plugin_name)) > 0) + constraint plugin_name_must_be_lowercase + check(lower(trim(plugin_name)) = plugin_name) + constraint plugin_host_name_uq + unique, + id_prefix text not null + constraint plugin_id_prefix_must_be_not_empty + check(length(trim(id_prefix)) > 0) + constraint plugin_id_prefix_must_fit_format + check (id_prefix ~ '^[a-z0-9]*$') + constraint plugin_host_id_prefix_uq + unique, + constraint plugin_fkey + foreign key (scope_id, public_id) + references plugin(scope_id, public_id) + on delete cascade + on update cascade, + constraint plugin_host_scope_id_name_uq + unique(scope_id, name) + ); + + create trigger update_version_column after update on plugin_host + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on plugin_host + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on plugin_host + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on plugin_host + for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); + + create trigger insert_plugin_subtype before insert on plugin_host + for each row execute procedure insert_plugin_subtype(); + + create trigger update_plugin_subtype before update on plugin_host + for each row execute procedure update_plugin_subtype(); + + create trigger delete_plugin_subtype after delete on plugin_host + for each row execute procedure delete_plugin_subtype(); + + insert into oplog_ticket (name, version) + values + ('plugin_host', 1); +`), + 16003: []byte(` +-- We are adding the name to the base host catalog type. This allows the db + -- to ensure that catalog names are unique in a scope across all subtypes. + alter table host_catalog + add column name wt_name; + + alter table host_catalog + add constraint host_catalog_scope_id_name_uq + unique (scope_id, name); + + -- Now that we've added the name column to the base type, we copy + -- the name from the static host catalog table into the base table. + update host_catalog + set name = st.name + from + static_host_catalog st + where + host_catalog.public_id = st.public_id and + st.name is not null; + + -- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql + -- to include the name. + -- insert_host_catalog_subtype() is a before insert trigger + -- function for subtypes of host_catalog + create or replace function insert_host_catalog_subtype() + returns trigger + as $$ + begin + insert into host_catalog + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_host_catalog_subtype() is + 'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table'; + + -- Now that we are tracking the name, which is mutable, we need to also + -- update the base table when the subtype tables are updated. + -- update_host_catalog_subtype() is intended to be used as a before update + -- trigger for all host catalog sub types. The purpose is to ensure that the + -- base table for host catalog to contain the updated names for each host catalog + -- in order to enforce uniqueness across all host catalogs, regardless of subtype, + -- in a given scope. + create function update_host_catalog_subtype() + returns trigger + as $$ + begin + update host_catalog set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_host_catalog_subtype() is + 'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type'; + + create trigger update_host_catalog_subtype before update on static_host_catalog + for each row execute procedure update_host_catalog_subtype(); +`), + 16004: []byte(` +/* + ┌──────────────────┐ + │ plugin_host │ + ├──────────────────┤ + │public_id (pk) │ + │... │ + └──────────────────┘ + ┼ + ┼ + ○ + ╱│╲ + ┌─────────────────────┐ + │ host_plugin_catalog │ + ├─────────────────────┤ ┌───────────────────────────┐ + ┌────────────────┐ │public_id (pk) │ │host_plugin_catalog_secret │ + │host_catalog │ │plugin_id (fk) │ ├───────────────────────────┤ + ├────────────────┤ │scope_id (fk) │ │host_catalog_id (pk, fk) │ + │public_id │┼┼──○┼│name │┼┼──○┼│secret │ + │scope_id │ │description │ │key_id (fk) │ + └────────────────┘ │attributes │ └───────────────────────────┘ + ┼ └─────────────────────┘ + ┼ ┼ + │ ┼ + │ ○ + ○ ╱│╲ + ╱│╲ ┌────────────────────┐ + ┌────────────────┐ │ host_plugin_set │ + │ host_set │ ├────────────────────┤ + ├────────────────┤ │public_id (pk) │ + │public_id │ │host_catalog_id (fk)│ + │host_catalog_id │┼┼──○┼│name │ + └────────────────┘ │description │ + │attributes │ + └────────────────────┘ + +*/ + + create table host_plugin_catalog ( + public_id wt_public_id primary key, + scope_id wt_scope_id not null + constraint iam_scope_fkey + references iam_scope (public_id) + on delete cascade + on update cascade, + plugin_id wt_plugin_id not null + constraint plugin_host_fkey + references plugin_host (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + attributes bytea not null + constraint attributes_must_not_empty + check(length(attributes) > 0), + constraint host_catalog_fkey + foreign key (scope_id, public_id) + references host_catalog (scope_id, public_id) + on delete cascade + on update cascade, + constraint host_plugin_catalog_scope_id_name_uq + unique(scope_id, name) + ); + + create trigger update_version_column after update on host_plugin_catalog + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_catalog + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_catalog + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_catalog + for each row execute procedure immutable_columns('public_id', 'scope_id', 'plugin_id', 'create_time'); + + create trigger insert_host_catalog_subtype before insert on host_plugin_catalog + for each row execute procedure insert_host_catalog_subtype(); + + create trigger update_host_catalog_subtype before update on host_plugin_catalog + for each row execute procedure update_host_catalog_subtype(); + + create trigger delete_host_catalog_subtype after delete on host_plugin_catalog + for each row execute procedure delete_host_catalog_subtype(); + + create table host_plugin_catalog_secret ( + catalog_id wt_public_id primary key + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + create_time wt_timestamp, + update_time wt_timestamp, + secret bytea not null -- encrypted value + constraint secret_must_not_be_empty + check(length(secret) > 0), + key_id wt_private_id not null + constraint kms_database_key_version_fkey + references kms_database_key_version (private_id) + on delete restrict + on update cascade + ); + + create trigger update_time_column before update on host_plugin_catalog_secret + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_catalog_secret + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_catalog_secret + for each row execute procedure immutable_columns('catalog_id', 'create_time'); + + create table host_plugin_set ( + public_id wt_public_id primary key, + catalog_id wt_public_id not null + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + attributes bytea not null + constraint attributes_must_not_empty + check(length(attributes) > 0), + constraint host_plugin_set_catalog_id_name_uq + unique(catalog_id, name), + constraint host_set_fkey + foreign key (catalog_id, public_id) + references host_set (catalog_id, public_id) + on delete cascade + on update cascade, + constraint host_plugin_set_catalog_id_public_id_uq + unique(catalog_id, public_id) + ); + + create trigger update_version_column after update on host_plugin_set + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_set + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_set + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_set + for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + + create trigger insert_host_set_subtype before insert on host_plugin_set + for each row execute procedure insert_host_set_subtype(); + + create trigger delete_host_set_subtype after delete on host_plugin_set + for each row execute procedure delete_host_set_subtype(); + + insert into oplog_ticket (name, version) + values + ('host_plugin_catalog', 1), + ('host_plugin_catalog_secret', 1), + ('host_plugin_set', 1); `), 2001: []byte(` -- log_migration entries represent logs generated during migrations diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go new file mode 100644 index 000000000..878e16274 --- /dev/null +++ b/internal/host/plugin/host_catalog.go @@ -0,0 +1,66 @@ +// Package plugin provides a plugin host catalog, and plugin host set resource +// which are used to interact with a host plugin as well as a repository to +// perform CRUDL and custom actions on these resource types. +package plugin + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "google.golang.org/protobuf/proto" +) + +// A HostCatalog contains plugin host sets. It is owned by +// a scope. +type HostCatalog struct { + *store.HostCatalog + tableName string `gorm:"-"` +} + +// NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId +// and pluginId. Name and description are the only valid options. All other +// options are ignored. +func NewHostCatalog(ctx context.Context, pluginId, scopeId string, opt ...Option) (*HostCatalog, error) { + const op = "plugin.NewHostCatalog" + opts := getOpts(opt...) + hc := &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: pluginId, + ScopeId: scopeId, + Name: opts.withName, + Description: opts.withDescription, + }, + } + + if opts.withAttributes != nil { + attrs, err := json.Marshal(opts.withAttributes) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) + } + hc.Attributes = attrs + } + return hc, nil +} + +func (c *HostCatalog) clone() *HostCatalog { + cp := proto.Clone(c.HostCatalog) + return &HostCatalog{ + HostCatalog: cp.(*store.HostCatalog), + } +} + +// TableName returns the table name for the host catalog. +func (c *HostCatalog) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "host_plugin_catalog" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (c *HostCatalog) SetTableName(n string) { + c.tableName = n +} diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go new file mode 100644 index 000000000..74ecacd43 --- /dev/null +++ b/internal/host/plugin/host_catalog_secret.go @@ -0,0 +1,92 @@ +package plugin + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/hashicorp/go-kms-wrapping/structwrapping" +) + +// HostCatalogSecret contains the encrypted secret for a host catalog. +// It is owned by a HostCatalog. +type HostCatalogSecret struct { + *store.HostCatalogSecret + tableName string `gorm:"-"` +} + +// newHostCatalogSecret creates an in memory host catalog secret. +// All options are ignored. +func newHostCatalogSecret(ctx context.Context, catalogId string, attributes map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) { + const op = "plugin.newHostCatlogSecret" + hcs := &HostCatalogSecret{ + HostCatalogSecret: &store.HostCatalogSecret{ + CatalogId: catalogId, + }, + } + + if attributes != nil { + attrs, err := json.Marshal(attributes) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) + } + hcs.Secret = attrs + } + return hcs, nil +} + +// TableName returns the table name for the host catalog. +func (c *HostCatalogSecret) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "host_plugin_catalog_secret" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (c *HostCatalogSecret) SetTableName(n string) { + c.tableName = n +} + +func (c *HostCatalogSecret) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { + const op = "plugin.(HostCatalogSecret).encrypt" + if len(c.Secret) == 0 { + errors.New(ctx, errors.InvalidParameter, op, "no attributes defined") + } + if err := structwrapping.WrapStruct(ctx, cipher, c.HostCatalogSecret, nil); err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt)) + } + c.KeyId = cipher.KeyID() + c.Secret = nil + return nil +} + +func (c *HostCatalogSecret) decrypt(ctx context.Context, cipher wrapping.Wrapper) error { + const op = "plugin.(HostCatalogSecret).decrypt" + if err := structwrapping.UnwrapStruct(ctx, cipher, c.HostCatalogSecret, nil); err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt)) + } + c.CtSecret = nil + return nil +} + +func (c *HostCatalogSecret) insertQuery() (query string, queryValues []interface{}) { + query = upsertHostCatalogSecretQuery + queryValues = []interface{}{ + c.CatalogId, + c.CtSecret, + c.KeyId, + } + return +} + +func (c *HostCatalogSecret) deleteQuery() (query string, queryValues []interface{}) { + query = deleteHostCatalogSecretQuery + queryValues = []interface{}{ + c.CatalogId, + } + return +} diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go new file mode 100644 index 000000000..243485efa --- /dev/null +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -0,0 +1,163 @@ +package plugin + +import ( + "context" + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestHostCatalogSecret_New(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "test") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + + type args struct { + catalogId string + attrs map[string]interface{} + } + + tests := []struct { + name string + args args + want *HostCatalogSecret + wantErr bool + wantEncryptErr bool + }{ + { + name: "blank-catalog-id", + args: args{ + catalogId: "", + }, + want: &HostCatalogSecret{ + HostCatalogSecret: &store.HostCatalogSecret{}, + }, + wantEncryptErr: true, + }, + { + name: "no-attributes", + args: args{ + catalogId: cat.GetPublicId(), + }, + want: &HostCatalogSecret{ + HostCatalogSecret: &store.HostCatalogSecret{ + CatalogId: cat.GetPublicId(), + }, + }, + wantEncryptErr: true, + }, + { + name: "valid", + args: args{ + catalogId: cat.GetPublicId(), + attrs: map[string]interface{}{"foo": "bar"}, + }, + want: &HostCatalogSecret{ + HostCatalogSecret: &store.HostCatalogSecret{ + CatalogId: cat.GetPublicId(), + Secret: func() []byte { + b, err := json.Marshal(map[string]interface{}{"foo": "bar"}) + require.NoError(t, err) + return b + }(), + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + require.NotNil(t, databaseWrapper) + + got, err := newHostCatalogSecret(context.Background(), tt.args.catalogId, tt.args.attrs) + if tt.wantErr { + assert.Error(t, err) + require.Nil(t, got) + return + } + require.NoError(t, err) + require.NotNil(t, got) + + assert.Empty(t, got.CtSecret) + assert.Equal(t, tt.want, got) + + err = got.encrypt(ctx, databaseWrapper) + if tt.wantEncryptErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NoError(t, got.decrypt(ctx, databaseWrapper)) + }) + } +} + +func TestHostCatalogSecret_Custom_Queries(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) + + hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"}) + require.NoError(t, err) + assert.NoError(t, hcs.encrypt(ctx, databaseWrapper)) + q, v := hcs.insertQuery() + _, err = rw.Exec(ctx, q, v) + assert.NoError(t, err) + + found, err := newHostCatalogSecret(ctx, cat.GetPublicId(), nil) + assert.NoError(t, rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId())) + require.NoError(t, found.decrypt(ctx, databaseWrapper)) + require.NoError(t, hcs.decrypt(ctx, databaseWrapper)) + // update the created/updated time from the original + hcs.CreateTime, hcs.UpdateTime = found.CreateTime, found.UpdateTime + assert.Empty(t, cmp.Diff(hcs, found, protocmp.Transform())) + + // Update the secret and see the value updated. + updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"updated": "value"}) + require.NoError(t, err) + assert.NoError(t, updated.encrypt(ctx, databaseWrapper)) + q, v = updated.insertQuery() + _, err = rw.Exec(ctx, q, v) + assert.NoError(t, err) + + assert.NoError(t, rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId())) + require.NoError(t, found.decrypt(ctx, databaseWrapper)) + require.NoError(t, updated.decrypt(ctx, databaseWrapper)) + // set the created time to the first and update time from the newly found + updated.CreateTime, updated.UpdateTime = hcs.CreateTime, found.UpdateTime + assert.Empty(t, cmp.Diff(updated, found, protocmp.Transform())) + + // Try to delete this secret. + q, v = updated.deleteQuery() + _, err = rw.Exec(ctx, q, v) + assert.NoError(t, err) + err = rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId()) + assert.Error(t, err) + assert.True(t, errors.IsNotFoundError(err)) +} diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go new file mode 100644 index 000000000..c3244b6cd --- /dev/null +++ b/internal/host/plugin/host_catalog_test.go @@ -0,0 +1,326 @@ +package plugin + +import ( + "context" + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestHostCatalog_Create(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + + type args struct { + pluginId string + scopeId string + opts []Option + } + + tests := []struct { + name string + args args + want *HostCatalog + wantErr bool + wantCreateErr bool + }{ + { + name: "blank-scopeId", + args: args{ + pluginId: plg.GetPublicId(), + scopeId: "", + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + wantCreateErr: true, + }, + { + name: "blank-pluginId", + args: args{ + pluginId: "", + scopeId: prj.GetPublicId(), + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + wantCreateErr: true, + }, + { + name: "valid-no-options", + args: args{ + pluginId: plg.GetPublicId(), + scopeId: prj.GetPublicId(), + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + ScopeId: prj.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-name", + args: args{ + pluginId: plg.GetPublicId(), + scopeId: prj.GetPublicId(), + opts: []Option{ + WithName("test-name"), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + ScopeId: prj.GetPublicId(), + Name: "test-name", + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-description", + args: args{ + pluginId: plg.GetPublicId(), + scopeId: prj.GetPublicId(), + opts: []Option{ + WithDescription("test-description"), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + ScopeId: prj.GetPublicId(), + Description: "test-description", + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-attributes", + args: args{ + pluginId: plg.GetPublicId(), + scopeId: prj.GetPublicId(), + opts: []Option{ + WithAttributes(map[string]interface{}{"foo": "bar"}), + }, + }, + want: func() *HostCatalog { + hc := &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + ScopeId: prj.GetPublicId(), + }, + } + var err error + hc.Attributes, err = json.Marshal(map[string]interface{}{"foo": "bar"}) + require.NoError(t, err) + return hc + }(), + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got, err := NewHostCatalog(context.Background(), tt.args.pluginId, tt.args.scopeId, tt.args.opts...) + require.NoError(t, err) + require.NotNil(t, got) + + assert.Emptyf(t, got.PublicId, "PublicId set") + assert.Equal(t, tt.want, got) + + id, err := newHostCatalogId() + assert.NoError(t, err) + + tt.want.PublicId = id + got.PublicId = id + + w := db.New(conn) + err = w.Create(context.Background(), got) + if tt.wantCreateErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + found := &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PublicId: id, + }, + } + require.NoError(t, w.LookupById(context.Background(), found)) + assert.Empty(t, cmp.Diff(got.HostCatalog, found.HostCatalog, protocmp.Transform()), "%q compared to %q", got.Attributes, found.Attributes) + } + }) + } +} + +func TestHostCatalog_Create_DuplicateNames(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + _, prj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test1", "prefix1") + plg2 := host.TestPlugin(t, conn, "test2", "prefix2") + + got, err := NewHostCatalog(context.Background(), plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + require.NoError(t, err) + got.PublicId, err = newHostCatalogId() + require.NoError(t, err) + w.Create(ctx, got) + + // Can't create another resource with the same pluginName in the same scope + got.PublicId, err = newHostCatalogId() + require.NoError(t, err) + assert.Error(t, w.Create(ctx, got)) + + // Can't create another resource with same pluginName in same scope even for different plugin + got, err = NewHostCatalog(context.Background(), plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + require.NoError(t, err) + got.PublicId, err = newHostCatalogId() + require.NoError(t, err) + assert.Error(t, w.Create(ctx, got)) + + // Can create another resource with same pluginName in different scope even for same plugin + got, err = NewHostCatalog(context.Background(), plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate")) + require.NoError(t, err) + got.PublicId, err = newHostCatalogId() + require.NoError(t, err) + assert.NoError(t, w.Create(ctx, got)) +} + +func TestHostCatalog_Delete(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + ignoredCat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + _ = ignoredCat + tests := []struct { + name string + cat *HostCatalog + rowsDeleted int + }{ + { + name: "valid", + cat: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PublicId: cat.GetPublicId(), + }, + }, + rowsDeleted: 1, + }, + { + name: "bad-id", + cat: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PublicId: "bad-id", + }, + }, + rowsDeleted: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + w := db.New(conn) + deletedRows, err := w.Delete(ctx, tt.cat) + require.NoError(t, err) + assert.Equal(t, tt.rowsDeleted, deletedRows) + + err = w.LookupById(ctx, tt.cat) + require.Error(t, err) + assert.True(t, errors.IsNotFoundError(err)) + }) + } +} + +func TestHostCatalog_Delete_Cascading(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + ctx := context.Background() + + t.Run("delete-scope", func(t *testing.T) { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "deletescope", "deletescope") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + + deleted, err := w.Delete(ctx, prj) + require.NoError(t, err) + require.Equal(t, 1, deleted) + + err = w.LookupById(ctx, cat) + require.Error(t, err) + assert.True(t, errors.IsNotFoundError(err)) + }) + + t.Run("delete-plugin", func(t *testing.T) { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "deleteplugin", "deleteplugin") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + + deleted, err := w.Delete(ctx, plg) + require.NoError(t, err) + require.Equal(t, 1, deleted) + + err = w.LookupById(ctx, cat) + require.Error(t, err) + assert.True(t, errors.IsNotFoundError(err)) + }) +} + +func TestHostCatalog_SetTableName(t *testing.T) { + defaultTableName := "host_plugin_catalog" + tests := []struct { + name string + initialName string + setNameTo string + want string + }{ + { + name: "new-name", + initialName: "", + setNameTo: "new-name", + want: "new-name", + }, + { + name: "reset to default", + initialName: "initial", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + def, err := NewHostCatalog(context.Background(), "", "") + require.NoError(t, err) + require.Equal(t, defaultTableName, def.TableName()) + s := &HostCatalog{ + HostCatalog: &store.HostCatalog{}, + tableName: tt.initialName, + } + s.SetTableName(tt.setNameTo) + assert.Equal(t, tt.want, s.TableName()) + }) + } +} diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go new file mode 100644 index 000000000..e40290bbb --- /dev/null +++ b/internal/host/plugin/host_set.go @@ -0,0 +1,53 @@ +package plugin + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" +) + +// A HostSet is a collection of hosts from the set's catalog. +type HostSet struct { + *store.HostSet + tableName string `gorm:"-"` +} + +// NewHostSet creates a new in memory HostSet assigned to catalogId. +// Attributes, name, and description are the only valid options. All other +// options are ignored. +func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, error) { + const op = "plugin.NewHostSet" + opts := getOpts(opt...) + set := &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalogId, + Name: opts.withName, + Description: opts.withDescription, + }, + } + + if opts.withAttributes != nil { + attrs, err := json.Marshal(opts.withAttributes) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) + } + set.Attributes = attrs + } + return set, nil +} + +// TableName returns the table name for the host set. +func (s *HostSet) TableName() string { + if s.tableName != "" { + return s.tableName + } + return "host_plugin_set" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (s *HostSet) SetTableName(n string) { + s.tableName = n +} diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go new file mode 100644 index 000000000..7a31b75f4 --- /dev/null +++ b/internal/host/plugin/host_set_test.go @@ -0,0 +1,201 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHostSet_Create(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + cat2 := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + + type args struct { + catalogId string + opts []Option + } + + tests := []struct { + name string + args args + want *HostSet + wantErr bool + }{ + { + name: "blank-catalogId", + args: args{ + catalogId: "", + }, + want: &HostSet{HostSet: &store.HostSet{ + Attributes: []byte("{}"), + }}, + wantErr: true, + }, + { + name: "valid-no-options", + args: args{ + catalogId: cat.GetPublicId(), + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-name", + args: args{ + catalogId: cat.GetPublicId(), + opts: []Option{ + WithName("test-name"), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + Name: "test-name", + Attributes: []byte("{}"), + }, + }, + }, + { + name: "duplicate-name", + args: args{ + catalogId: cat.GetPublicId(), + opts: []Option{ + WithName("test-name"), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + Name: "test-name", + Attributes: []byte("{}"), + }, + }, + wantErr: true, + }, + { + name: "valid-duplicate-name-different-catalog", + args: args{ + catalogId: cat2.GetPublicId(), + opts: []Option{ + WithName("test-name"), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat2.GetPublicId(), + Name: "test-name", + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-description", + args: args{ + catalogId: cat.GetPublicId(), + opts: []Option{ + WithDescription("test-description"), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + Description: "test-description", + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-attributes", + args: args{ + catalogId: cat.GetPublicId(), + opts: []Option{ + WithAttributes(map[string]interface{}{"foo": "bar"}), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + Attributes: []byte(`{"foo":"bar"}`), + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + got, err := NewHostSet(ctx, tt.args.catalogId, tt.args.opts...) + require.NoError(t, err) + require.NotNil(t, got) + assert.Emptyf(t, got.PublicId, "PublicId set") + assert.Equal(t, tt.want, got) + + id, err := newHostSetId() + assert.NoError(t, err) + + tt.want.PublicId = id + got.PublicId = id + + w := db.New(conn) + err = w.Create(context.Background(), got) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestHostSet_SetTableName(t *testing.T) { + defaultTableName := "host_plugin_set" + tests := []struct { + name string + initialName string + setNameTo string + want string + }{ + { + name: "new-name", + initialName: "", + setNameTo: "new-name", + want: "new-name", + }, + { + name: "reset to default", + initialName: "initial", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + def := &HostSet{ + HostSet: &store.HostSet{}, + } + require.Equal(defaultTableName, def.TableName()) + s := &HostSet{ + HostSet: &store.HostSet{}, + tableName: tt.initialName, + } + s.SetTableName(tt.setNameTo) + assert.Equal(tt.want, s.TableName()) + }) + } +} diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go new file mode 100644 index 000000000..c4d2af7e7 --- /dev/null +++ b/internal/host/plugin/ids.go @@ -0,0 +1,41 @@ +package plugin + +import ( + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/types/subtypes" +) + +func init() { + if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix); err != nil { + panic(err) + } +} + +// PublicId prefixes for the resources in the plugin package. +const ( + // TODO: Pull these out of being constants and have them derivable at run time. + HostCatalogPrefix = "hcplg" + HostSetPrefix = "hsplg" + + Subtype = subtypes.Subtype("plugin") +) + +func newHostCatalogId() (string, error) { + // TODO: Take in a prefix portion as an argument. + id, err := db.NewPublicId(HostCatalogPrefix) + if err != nil { + return "", errors.WrapDeprecated(err, "plugin.newHostCatalogId") + } + return id, nil +} + +func newHostSetId() (string, error) { + // TODO: Take in a prefix portion as an argument. + id, err := db.NewPublicId(HostSetPrefix) + if err != nil { + return "", errors.WrapDeprecated(err, "plugin.newHostSetId") + } + return id, nil +} diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go new file mode 100644 index 000000000..677ece4f3 --- /dev/null +++ b/internal/host/plugin/immutable_fields_test.go @@ -0,0 +1,163 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestPluginCatalog_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + o, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + plg2 := host.TestPlugin(t, conn, "test2", "prefix2") + new := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + + tests := []struct { + name string + update *HostCatalog + fieldMask []string + }{ + { + name: "public_id", + update: func() *HostCatalog { + c := new.clone() + c.PublicId = "hc_thisIsNotAValidId" + return c + }(), + fieldMask: []string{"PublicId"}, + }, + { + name: "create time", + update: func() *HostCatalog { + c := new.clone() + c.CreateTime = &ts + return c + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "scope id", + update: func() *HostCatalog { + c := new.clone() + c.ScopeId = o.PublicId + return c + }(), + fieldMask: []string{"ScopeId"}, + }, + { + name: "plugin id", + update: func() *HostCatalog { + c := new.clone() + c.PluginId = plg2.GetPublicId() + return c + }(), + fieldMask: []string{"PluginId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.clone() + err := w.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.clone() + err = w.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig, after)) + }) + } +} + +func TestPluginSet_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test", "prefix") + cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + new := TestSet(t, conn, cat.GetPublicId()) + + tests := []struct { + name string + update *HostSet + fieldMask []string + }{ + { + name: "public_id", + update: func() *HostSet { + c := new.testCloneHostSet() + c.PublicId = "hc_thisIsNotAValidId" + return c + }(), + fieldMask: []string{"PublicId"}, + }, + { + name: "create time", + update: func() *HostSet { + c := new.testCloneHostSet() + c.CreateTime = &ts + return c + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "plugin_host_catalog_id", + update: func() *HostSet { + c := new.testCloneHostSet() + c.CatalogId = "stc_01234567890" + return c + }(), + fieldMask: []string{"CatalogId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.testCloneHostSet() + err := w.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.testCloneHostSet() + err = w.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig, after)) + }) + } +} + +func (c *HostSet) testCloneHostSet() *HostSet { + cp := proto.Clone(c.HostSet) + return &HostSet{ + HostSet: cp.(*store.HostSet), + } +} diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go new file mode 100644 index 000000000..df3436ba5 --- /dev/null +++ b/internal/host/plugin/options.go @@ -0,0 +1,59 @@ +package plugin + +// getOpts - iterate the inbound Options and return a struct +func getOpts(opt ...Option) options { + opts := getDefaultOptions() + for _, o := range opt { + o(&opts) + } + return opts +} + +// Option - how Options are passed as arguments. +type Option func(*options) + +// options = how options are represented +type options struct { + withName string + withDescription string + withLimit int + withAttributes map[string]interface{} +} + +func getDefaultOptions() options { + return options{ + withDescription: "", + withName: "", + withAttributes: make(map[string]interface{}), + } +} + +// WithDescription provides an optional description. +func WithDescription(desc string) Option { + return func(o *options) { + o.withDescription = desc + } +} + +// WithName provides an optional name. +func WithName(name string) Option { + return func(o *options) { + o.withName = name + } +} + +// WithAttributes provides an optional attributes field. +func WithAttributes(attrs map[string]interface{}) Option { + return func(o *options) { + o.withAttributes = attrs + } +} + +// WithLimit provides an option to provide a limit. Intentionally allowing +// negative integers. If WithLimit < 0, then unlimited results are +// returned. If WithLimit == 0, then default limits are used for results. +func WithLimit(l int) Option { + return func(o *options) { + o.withLimit = l + } +} diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go new file mode 100644 index 000000000..4cda5a9ff --- /dev/null +++ b/internal/host/plugin/options_test.go @@ -0,0 +1,29 @@ +package plugin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithName", func(t *testing.T) { + opts := getOpts(WithName("test")) + testOpts := getDefaultOptions() + testOpts.withName = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithDescription", func(t *testing.T) { + opts := getOpts(WithDescription("test desc")) + testOpts := getDefaultOptions() + testOpts.withDescription = "test desc" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithLimit", func(t *testing.T) { + opts := getOpts(WithLimit(5)) + testOpts := getDefaultOptions() + testOpts.withLimit = 5 + assert.Equal(t, opts, testOpts) + }) +} diff --git a/internal/host/plugin/query.go b/internal/host/plugin/query.go new file mode 100644 index 000000000..bf3db9dfe --- /dev/null +++ b/internal/host/plugin/query.go @@ -0,0 +1,19 @@ +package plugin + +const ( + upsertHostCatalogSecretQuery = ` +insert into host_plugin_catalog_secret + (catalog_id, secret, key_id) +values + ($1, $2, $3) +on conflict (catalog_id) do update + set secret = excluded.secret, + key_id = excluded.key_id +returning *; +` + + deleteHostCatalogSecretQuery = ` +delete from host_plugin_catalog_secret + where catalog_id = $1; +` +) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go new file mode 100644 index 000000000..c9a0ea5db --- /dev/null +++ b/internal/host/plugin/store/host.pb.go @@ -0,0 +1,566 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.15.8 +// source: controller/storage/host/plugin/store/v1/host.proto + +// Package store provides protobufs for storing types in the plugin host +// package. + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/known/structpb" + 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 HostCatalog struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + ScopeId string `protobuf:"bytes,6,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` + // The public id of the plugin this catalog uses. + // @inject_tag: `gorm:"not_null"` + PluginId string `protobuf:"bytes,7,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // attributes is a jsonb formatted field. + // @inject_tag: `gorm:"default:null"` + Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"` +} + +func (x *HostCatalog) Reset() { + *x = HostCatalog{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostCatalog) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostCatalog) ProtoMessage() {} + +func (x *HostCatalog) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 HostCatalog.ProtoReflect.Descriptor instead. +func (*HostCatalog) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{0} +} + +func (x *HostCatalog) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *HostCatalog) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *HostCatalog) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *HostCatalog) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *HostCatalog) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *HostCatalog) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *HostCatalog) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *HostCatalog) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *HostCatalog) GetAttributes() []byte { + if x != nil { + return x.Attributes + } + return nil +} + +type HostSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // attributes is a jsonb formatted field. + // @inject_tag: `gorm:"default:null"` + Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"` +} + +func (x *HostSet) Reset() { + *x = HostSet{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostSet) ProtoMessage() {} + +func (x *HostSet) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 HostSet.ProtoReflect.Descriptor instead. +func (*HostSet) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1} +} + +func (x *HostSet) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *HostSet) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *HostSet) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *HostSet) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *HostSet) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *HostSet) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + +func (x *HostSet) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *HostSet) GetAttributes() []byte { + if x != nil { + return x.Attributes + } + return nil +} + +type HostCatalogSecret struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // catalog_id is the public id of the catalog containing this secret. + // @inject_tag: `gorm:"primary_key"` + CatalogId string `protobuf:"bytes,1,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // attributes is the plain-text of the attribute data. We are not storing + // this plain-text value in the database. + // @inject_tag: `gorm:"-" wrapping:"pt,secret_data"` + Secret []byte `protobuf:"bytes,4,opt,name=secret,proto3" json:"secret,omitempty" gorm:"-" wrapping:"pt,secret_data"` + // ct_attributes is the ciphertext of the attribute data stored in the db. + // @inject_tag: `gorm:"column:secret;not_null" wrapping:"ct,secret_data"` + CtSecret []byte `protobuf:"bytes,5,opt,name=ct_secret,json=ctSecret,proto3" json:"ct_secret,omitempty" gorm:"column:secret;not_null" wrapping:"ct,secret_data"` + // The key_id of the kms database key used for encrypting this entry. + // It must be set. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,6,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` +} + +func (x *HostCatalogSecret) Reset() { + *x = HostCatalogSecret{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostCatalogSecret) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostCatalogSecret) ProtoMessage() {} + +func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 HostCatalogSecret.ProtoReflect.Descriptor instead. +func (*HostCatalogSecret) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2} +} + +func (x *HostCatalogSecret) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + +func (x *HostCatalogSecret) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *HostCatalogSecret) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *HostCatalogSecret) GetSecret() []byte { + if x != nil { + return x.Secret + } + return nil +} + +func (x *HostCatalogSecret) GetCtSecret() []byte { + if x != nil { + return x.CtSecret + } + return nil +} + +func (x *HostCatalogSecret) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +var File_controller_storage_host_plugin_store_v1_host_proto protoreflect.FileDescriptor + +var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ + 0x0a, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 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, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x03, 0x0a, 0x0b, 0x48, 0x6f, 0x73, + 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, + 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x81, 0x03, 0x0a, 0x07, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x98, 0x02, + 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_controller_storage_host_plugin_store_v1_host_proto_rawDescOnce sync.Once + file_controller_storage_host_plugin_store_v1_host_proto_rawDescData = file_controller_storage_host_plugin_store_v1_host_proto_rawDesc +) + +func file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP() []byte { + file_controller_storage_host_plugin_store_v1_host_proto_rawDescOnce.Do(func() { + file_controller_storage_host_plugin_store_v1_host_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_host_plugin_store_v1_host_proto_rawDescData) + }) + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescData +} + +var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{ + (*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog + (*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet + (*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret + (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{ + 3, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_controller_storage_host_plugin_store_v1_host_proto_init() } +func file_controller_storage_host_plugin_store_v1_host_proto_init() { + if File_controller_storage_host_plugin_store_v1_host_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostCatalog); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostCatalogSecret); 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_controller_storage_host_plugin_store_v1_host_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_host_plugin_store_v1_host_proto_goTypes, + DependencyIndexes: file_controller_storage_host_plugin_store_v1_host_proto_depIdxs, + MessageInfos: file_controller_storage_host_plugin_store_v1_host_proto_msgTypes, + }.Build() + File_controller_storage_host_plugin_store_v1_host_proto = out.File + file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = nil + file_controller_storage_host_plugin_store_v1_host_proto_goTypes = nil + file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = nil +} diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go new file mode 100644 index 000000000..9dad4eae6 --- /dev/null +++ b/internal/host/plugin/testing.go @@ -0,0 +1,50 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/jinzhu/gorm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCatalog creates count number of plugin host catalogs to the provided DB +// with the provided scope id. If any errors are encountered during the creation of +// the host catalog, the test will fail. +func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...Option) *HostCatalog { + t.Helper() + ctx := context.Background() + cat, err := NewHostCatalog(ctx, pluginId, scopeId, opt...) + require.NoError(t, err) + assert.NotNil(t, cat) + id, err := newHostCatalogId() + assert.NoError(t, err) + assert.NotEmpty(t, id) + cat.PublicId = id + + w := db.New(conn) + require.NoError(t, w.Create(ctx, cat)) + return cat +} + +// TestSet creates count number of plugin host sets in the provided DB +// with the provided catalog id. The catalog must have been created +// previously. The test will fail if any errors are encountered. +func TestSet(t *testing.T, conn *gorm.DB, catalogId string, opt ...Option) *HostSet { + t.Helper() + ctx := context.Background() + assert := assert.New(t) + set, err := NewHostSet(ctx, catalogId, opt...) + require.NoError(t, err) + assert.NotNil(set) + id, err := newHostSetId() + assert.NoError(err) + assert.NotEmpty(id) + set.PublicId = id + + w := db.New(conn) + require.NoError(t, w.Create(ctx, set)) + return set +} diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go new file mode 100644 index 000000000..fe939e159 --- /dev/null +++ b/internal/host/plugin/testing_test.go @@ -0,0 +1,48 @@ +package plugin + +import ( + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_TestCatalogs(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(proj) + assert.NotEmpty(proj.GetPublicId()) + + plg := host.TestPlugin(t, conn, "test", "prefix") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) + + cs := TestCatalog(t, conn, plg.GetPublicId(), proj.GetPublicId(), WithName("foo"), WithDescription("bar")) + assert.NotEmpty(cs.GetPublicId()) + assert.Equal("foo", cs.GetName()) + assert.Equal("bar", cs.GetDescription()) +} + +func Test_TestSet(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + plg := host.TestPlugin(t, conn, "test", "prefix") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) + + c := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + set := TestSet(t, conn, c.GetPublicId(), WithName("foo"), WithDescription("bar")) + assert.NotEmpty(set.GetPublicId()) + assert.Equal("foo", set.GetName()) + assert.Equal("bar", set.GetDescription()) +} diff --git a/internal/plugin/doc.go b/internal/plugin/doc.go new file mode 100644 index 000000000..85e195c9f --- /dev/null +++ b/internal/plugin/doc.go @@ -0,0 +1,3 @@ +// Package plugin contains other packages related to the different plugin +// types and any helpers related to working with non type specific plugins. +package plugin diff --git a/internal/plugin/host/doc.go b/internal/plugin/host/doc.go new file mode 100644 index 000000000..3ca79e39c --- /dev/null +++ b/internal/plugin/host/doc.go @@ -0,0 +1,4 @@ +// Package host provides a plugin type used to interface with boundary's host +// related resources. Additionally it provides a repository for performing +// CRUDL and custom operations on this plugin type. +package host diff --git a/internal/plugin/host/ids.go b/internal/plugin/host/ids.go new file mode 100644 index 000000000..c042b3c2a --- /dev/null +++ b/internal/plugin/host/ids.go @@ -0,0 +1,19 @@ +package host + +import ( + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" +) + +// PublicId prefixes for the resources in the plugin package. +const ( + PluginPrefix = "plgh" +) + +func newPluginId() (string, error) { + id, err := db.NewPublicId(PluginPrefix) + if err != nil { + return "", errors.WrapDeprecated(err, "plugin.newPluginId") + } + return id, nil +} diff --git a/internal/plugin/host/immutable_fields_test.go b/internal/plugin/host/immutable_fields_test.go new file mode 100644 index 000000000..d4e48b6e8 --- /dev/null +++ b/internal/plugin/host/immutable_fields_test.go @@ -0,0 +1,85 @@ +package host + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/plugin/host/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestHostPlugin_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + plg := TestPlugin(t, conn, "test", "prefix") + + newPlugin := plg + + tests := []struct { + name string + update *Plugin + fieldMask []string + }{ + { + name: "public_id", + update: func() *Plugin { + c := newPlugin.testClonePlugin() + c.PublicId = "hc_thisIsNotAValidId" + return c + }(), + fieldMask: []string{"PublicId"}, + }, + { + name: "create time", + update: func() *Plugin { + c := newPlugin.testClonePlugin() + c.CreateTime = &ts + return c + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "pluginName", + update: func() *Plugin { + c := newPlugin.testClonePlugin() + c.PluginName = "different pluginName" + return c + }(), + fieldMask: []string{"PluginName"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := newPlugin.testClonePlugin() + err := w.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := newPlugin.testClonePlugin() + err = w.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig, after)) + }) + } +} + +func (c *Plugin) testClonePlugin() *Plugin { + cp := proto.Clone(c.Plugin) + return &Plugin{ + Plugin: cp.(*store.Plugin), + } +} diff --git a/internal/plugin/host/options.go b/internal/plugin/host/options.go new file mode 100644 index 000000000..3832ef15d --- /dev/null +++ b/internal/plugin/host/options.go @@ -0,0 +1,50 @@ +package host + +// getOpts - iterate the inbound Options and return a struct +func getOpts(opt ...Option) options { + opts := getDefaultOptions() + for _, o := range opt { + o(&opts) + } + return opts +} + +// Option - how Options are passed as arguments. +type Option func(*options) + +// options = how options are represented +type options struct { + withName string + withDescription string + withLimit int +} + +func getDefaultOptions() options { + return options{ + withDescription: "", + withName: "", + } +} + +// WithDescription provides an optional description. +func WithDescription(desc string) Option { + return func(o *options) { + o.withDescription = desc + } +} + +// WithName provides an optional name. +func WithName(name string) Option { + return func(o *options) { + o.withName = name + } +} + +// WithLimit provides an option to provide a limit. Intentionally allowing +// negative integers. If WithLimit < 0, then unlimited results are +// returned. If WithLimit == 0, then default limits are used for results. +func WithLimit(l int) Option { + return func(o *options) { + o.withLimit = l + } +} diff --git a/internal/plugin/host/options_test.go b/internal/plugin/host/options_test.go new file mode 100644 index 000000000..fed3d1c9f --- /dev/null +++ b/internal/plugin/host/options_test.go @@ -0,0 +1,29 @@ +package host + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithName", func(t *testing.T) { + opts := getOpts(WithName("test")) + testOpts := getDefaultOptions() + testOpts.withName = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithDescription", func(t *testing.T) { + opts := getOpts(WithDescription("test desc")) + testOpts := getDefaultOptions() + testOpts.withDescription = "test desc" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithLimit", func(t *testing.T) { + opts := getOpts(WithLimit(5)) + testOpts := getDefaultOptions() + testOpts.withLimit = 5 + assert.Equal(t, opts, testOpts) + }) +} diff --git a/internal/plugin/host/plugin.go b/internal/plugin/host/plugin.go new file mode 100644 index 000000000..ddabc58ec --- /dev/null +++ b/internal/plugin/host/plugin.go @@ -0,0 +1,43 @@ +package host + +import ( + "github.com/hashicorp/boundary/internal/plugin/host/store" + "github.com/hashicorp/boundary/internal/types/scope" +) + +// A Plugin enables additional logic to be used by boundary. +// It is owned by a scope. +type Plugin struct { + *store.Plugin + tableName string `gorm:"-"` +} + +// NewPlugin creates a new in memory Plugin assigned to the global scope. +// Name, Description are the only allowed option. All other options are ignored. +func NewPlugin(pluginName, idPrefix string, opt ...Option) *Plugin { + opts := getOpts(opt...) + p := &Plugin{ + Plugin: &store.Plugin{ + PluginName: pluginName, + IdPrefix: idPrefix, + ScopeId: scope.Global.String(), + Name: opts.withName, + Description: opts.withDescription, + }, + } + return p +} + +// TableName returns the table name for the host plugin. +func (c *Plugin) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "plugin_host" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (c *Plugin) SetTableName(n string) { + c.tableName = n +} diff --git a/internal/plugin/host/plugin_test.go b/internal/plugin/host/plugin_test.go new file mode 100644 index 000000000..01bccd8ae --- /dev/null +++ b/internal/plugin/host/plugin_test.go @@ -0,0 +1,275 @@ +package host + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/plugin/host/store" +) + +func TestPlugin_Create(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + + type args struct { + pluginName string + idPrefix string + semVer string + opts []Option + } + + tests := []struct { + name string + args args + want *Plugin + wantErr bool + }{ + { + name: "blank-pluginName", + args: args{ + idPrefix: "prefix", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + IdPrefix: "prefix", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "blank-idPrefix", + args: args{ + pluginName: "plugin name", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "plugin name", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "idprefix-capitalized", + args: args{ + pluginName: "idprefixcapitalized", + idPrefix: "IdPrefixCapitalized", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "idprefixcapitalized", + IdPrefix: "IdPrefixCapitalized", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "idprefix-space", + args: args{ + pluginName: "idprefix space", + idPrefix: "idprefix space", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "idprefix space", + IdPrefix: "idprefix space", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "pluginName-capitalized", + args: args{ + pluginName: "PluginNameCapitalized", + idPrefix: "pluginnamecapitalized", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "PluginNameCapitalized", + IdPrefix: "pluginnamecapitalized", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "valid-no-options", + args: args{ + pluginName: "validnooptions", + idPrefix: "validnooptions", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "validnooptions", + IdPrefix: "validnooptions", + ScopeId: scope.Global.String(), + }, + }, + }, + { + name: "valid-with-description", + args: args{ + pluginName: "validwithdescription", + idPrefix: "validwithdescription", + opts: []Option{ + WithDescription("description"), + }, + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "validwithdescription", + IdPrefix: "validwithdescription", + ScopeId: scope.Global.String(), + Description: "description", + }, + }, + }, + { + name: "valid-pluginName-name-option", + args: args{ + pluginName: "validpluginnamenameoption", + idPrefix: "validpluginnamenameoption", + opts: []Option{ + WithName("valid-pluginName-name-option"), + WithDescription("description"), + }, + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "validpluginnamenameoption", + IdPrefix: "validpluginnamenameoption", + ScopeId: scope.Global.String(), + Name: "valid-pluginName-name-option", + Description: "description", + }, + }, + }, + // This must be run after the "valid-no-options" test + { + name: "duplicate-pluginName", + args: args{ + pluginName: "validnooptions", + idPrefix: "duplicatepluginname", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "validnooptions", + IdPrefix: "duplicatepluginname", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "duplicate-idPrefix", + args: args{ + pluginName: "duplicateidprefix", + idPrefix: "validnooptions", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "duplicateidprefix", + IdPrefix: "validnooptions", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got := NewPlugin(tt.args.pluginName, tt.args.idPrefix, tt.args.opts...) + require.NotNil(t, got) + require.Emptyf(t, got.PublicId, "PublicId set") + + id, err := newPluginId() + require.NoError(t, err) + got.PublicId = id + + tt.want.PublicId = id + assert.Equal(t, tt.want, got) + + w := db.New(conn) + err = w.Create(context.Background(), got) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestPlugin_Update(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + ctx := context.Background() + plg := TestPlugin(t, conn, "delete-plugin", "prefix") + assert.Equal(t, uint32(1), plg.Version) + + plg.Name = "New" + plg.Description = "Description" + rowCount, err := w.Update(ctx, plg, []string{"Name", "Description"}, nil) + require.NoError(t, err) + assert.Equal(t, 1, rowCount) + + assert.Equal(t, uint32(2), plg.Version) + assert.Equal(t, "New", plg.Name) + assert.Equal(t, "Description", plg.Description) +} + +func TestPlugin_Delete(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + ctx := context.Background() + plg := TestPlugin(t, conn, "delete-plugin", "prefix") + + deleted, err := w.Delete(ctx, plg) + require.NoError(t, err) + require.Equal(t, 1, deleted) +} + +func TestPlugin_SetTableName(t *testing.T) { + defaultTableName := "plugin_host" + tests := []struct { + name string + initialName string + setNameTo string + want string + }{ + { + name: "new-pluginName", + initialName: "", + setNameTo: "new-pluginName", + want: "new-pluginName", + }, + { + name: "reset to default", + initialName: "initial", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + def := NewPlugin("", "prefix") + require.Equal(defaultTableName, def.TableName()) + s := &Plugin{ + Plugin: &store.Plugin{}, + tableName: tt.initialName, + } + s.SetTableName(tt.setNameTo) + assert.Equal(tt.want, s.TableName()) + }) + } +} diff --git a/internal/plugin/host/store/plugin.pb.go b/internal/plugin/host/store/plugin.pb.go new file mode 100644 index 000000000..05c63df53 --- /dev/null +++ b/internal/plugin/host/store/plugin.pb.go @@ -0,0 +1,267 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.15.8 +// source: controller/storage/plugin/host/store/v1/plugin.proto + +// Package store provides protobufs for storing types in the host package. + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + 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 Plugin struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,20,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + ScopeId string `protobuf:"bytes,60,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // plugin_name is required. The value must be unique across all host plugins + // @inject_tag: `gorm:"default:null"` + PluginName string `protobuf:"bytes,80,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty" gorm:"default:null"` + // id_prefix is required. The value must be unique across all host plugins + // @inject_tag: `gorm:"default:null"` + IdPrefix string `protobuf:"bytes,90,opt,name=id_prefix,json=idPrefix,proto3" json:"id_prefix,omitempty" gorm:"default:null"` +} + +func (x *Plugin) Reset() { + *x = Plugin{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Plugin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plugin) ProtoMessage() {} + +func (x *Plugin) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_plugin_host_store_v1_plugin_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 Plugin.ProtoReflect.Descriptor instead. +func (*Plugin) Descriptor() ([]byte, []int) { + return file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescGZIP(), []int{0} +} + +func (x *Plugin) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Plugin) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Plugin) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Plugin) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Plugin) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Plugin) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *Plugin) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Plugin) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *Plugin) GetIdPrefix() string { + if x != nil { + return x.IdPrefix + } + return "" +} + +var File_controller_storage_plugin_host_store_v1_plugin_proto protoreflect.FileDescriptor + +var file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = []byte{ + 0x0a, 0x34, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, + 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a, + 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, + 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, + 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x69, 0x64, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescOnce sync.Once + file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData = file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc +) + +func file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescGZIP() []byte { + file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescOnce.Do(func() { + file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData) + }) + return file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData +} + +var file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes = []interface{}{ + (*Plugin)(nil), // 0: controller.storage.plugin.host.store.v1.Plugin + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs = []int32{ + 1, // 0: controller.storage.plugin.host.store.v1.Plugin.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.plugin.host.store.v1.Plugin.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 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_controller_storage_plugin_host_store_v1_plugin_proto_init() } +func file_controller_storage_plugin_host_store_v1_plugin_proto_init() { + if File_controller_storage_plugin_host_store_v1_plugin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Plugin); 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_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes, + DependencyIndexes: file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs, + MessageInfos: file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes, + }.Build() + File_controller_storage_plugin_host_store_v1_plugin_proto = out.File + file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = nil + file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes = nil + file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs = nil +} diff --git a/internal/plugin/host/testing.go b/internal/plugin/host/testing.go new file mode 100644 index 000000000..ff585ed4b --- /dev/null +++ b/internal/plugin/host/testing.go @@ -0,0 +1,22 @@ +package host + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/jinzhu/gorm" + "github.com/stretchr/testify/require" +) + +func TestPlugin(t *testing.T, conn *gorm.DB, name string, prefix string) *Plugin { + t.Helper() + p := NewPlugin(name, prefix) + id, err := newPluginId() + require.NoError(t, err) + p.PublicId = id + + w := db.New(conn) + require.NoError(t, w.Create(context.Background(), p)) + return p +} diff --git a/internal/plugin/host/testing_test.go b/internal/plugin/host/testing_test.go new file mode 100644 index 000000000..916883773 --- /dev/null +++ b/internal/plugin/host/testing_test.go @@ -0,0 +1,18 @@ +package host + +import ( + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_TestPlugins(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + + plg := TestPlugin(t, conn, "test", "prefix") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) +} diff --git a/internal/plugin/immutable_fields_test.go b/internal/plugin/immutable_fields_test.go new file mode 100644 index 000000000..31a2299fe --- /dev/null +++ b/internal/plugin/immutable_fields_test.go @@ -0,0 +1,72 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/plugin/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestPlugin_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + plg := testPlugin(t, conn, "test") + + newPlugin := plg + + tests := []struct { + name string + update *plugin + fieldMask []string + }{ + { + name: "public_id", + update: func() *plugin { + c := newPlugin.testClonePlugin() + c.PublicId = "pi_thisIsNotAValidId" + return c + }(), + fieldMask: []string{"PublicId"}, + }, + { + name: "scope", + update: func() *plugin { + c := newPlugin.testClonePlugin() + c.ScopeId = "o_1234567890" + return c + }(), + fieldMask: []string{"ScopeId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := newPlugin.testClonePlugin() + err := w.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := newPlugin.testClonePlugin() + err = w.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig, after)) + }) + } +} + +func (c *plugin) testClonePlugin() *plugin { + cp := proto.Clone(c.Plugin) + return &plugin{ + Plugin: cp.(*store.Plugin), + } +} diff --git a/internal/plugin/store/plugin.pb.go b/internal/plugin/store/plugin.pb.go new file mode 100644 index 000000000..54d39c526 --- /dev/null +++ b/internal/plugin/store/plugin.pb.go @@ -0,0 +1,182 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.15.8 +// source: controller/storage/plugin/store/v1/plugin.proto + +// Package store provides protobufs for storing types in the host package. + +package store + +import ( + _ "github.com/hashicorp/boundary/internal/db/timestamp" + 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) +) + +// This is a typeless plugin. In practice this should never be used directly. +// This is included for testing purposes only. +type Plugin struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` +} + +func (x *Plugin) Reset() { + *x = Plugin{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Plugin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plugin) ProtoMessage() {} + +func (x *Plugin) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_plugin_store_v1_plugin_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 Plugin.ProtoReflect.Descriptor instead. +func (*Plugin) Descriptor() ([]byte, []int) { + return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{0} +} + +func (x *Plugin) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Plugin) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *Plugin) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_controller_storage_plugin_store_v1_plugin_proto protoreflect.FileDescriptor + +var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, 0x0a, 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x3b, 0x5a, 0x39, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_controller_storage_plugin_store_v1_plugin_proto_rawDescOnce sync.Once + file_controller_storage_plugin_store_v1_plugin_proto_rawDescData = file_controller_storage_plugin_store_v1_plugin_proto_rawDesc +) + +func file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP() []byte { + file_controller_storage_plugin_store_v1_plugin_proto_rawDescOnce.Do(func() { + file_controller_storage_plugin_store_v1_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_plugin_store_v1_plugin_proto_rawDescData) + }) + return file_controller_storage_plugin_store_v1_plugin_proto_rawDescData +} + +var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_plugin_store_v1_plugin_proto_goTypes = []interface{}{ + (*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin +} +var file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_storage_plugin_store_v1_plugin_proto_init() } +func file_controller_storage_plugin_store_v1_plugin_proto_init() { + if File_controller_storage_plugin_store_v1_plugin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Plugin); 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_controller_storage_plugin_store_v1_plugin_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_plugin_store_v1_plugin_proto_goTypes, + DependencyIndexes: file_controller_storage_plugin_store_v1_plugin_proto_depIdxs, + MessageInfos: file_controller_storage_plugin_store_v1_plugin_proto_msgTypes, + }.Build() + File_controller_storage_plugin_store_v1_plugin_proto = out.File + file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = nil + file_controller_storage_plugin_store_v1_plugin_proto_goTypes = nil + file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = nil +} diff --git a/internal/plugin/testing.go b/internal/plugin/testing.go new file mode 100644 index 000000000..01d5a3929 --- /dev/null +++ b/internal/plugin/testing.go @@ -0,0 +1,55 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/plugin/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/jinzhu/gorm" + "github.com/stretchr/testify/require" +) + +// A typeless plugin used for tests. +type plugin struct { + *store.Plugin + tableName string `gorm:"-"` +} + +// newPlugin is used in tests and creates a typeless plugin in the global scope. +func newPlugin(name string) *plugin { + p := &plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + Name: name, + }, + } + return p +} + +// TableName returns the table name for the host plugin. +func (c *plugin) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "plugin" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (c *plugin) SetTableName(n string) { + c.tableName = n +} + +func testPlugin(t *testing.T, conn *gorm.DB, name string) *plugin { + t.Helper() + p := newPlugin(name) + id, err := db.NewPublicId("plg") + require.NoError(t, err) + p.PublicId = id + + w := db.New(conn) + require.NoError(t, w.Create(context.Background(), p)) + return p +} diff --git a/internal/plugin/testing_test.go b/internal/plugin/testing_test.go new file mode 100644 index 000000000..a047dcc0a --- /dev/null +++ b/internal/plugin/testing_test.go @@ -0,0 +1,18 @@ +package plugin + +import ( + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_testPlugin(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + + plg := testPlugin(t, conn, "test") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) +} diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto new file mode 100644 index 000000000..e9cb28b64 --- /dev/null +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -0,0 +1,112 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the plugin host +// package. +package controller.storage.host.plugin.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/host/plugin/store;store"; + +import "google/protobuf/struct.proto"; +import "controller/storage/timestamp/v1/timestamp.proto"; +import "controller/custom_options/v1/options.proto"; + +message HostCatalog { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}]; + + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + string scope_id = 6; + + // The public id of the plugin this catalog uses. + // @inject_tag: `gorm:"not_null"` + string plugin_id = 7; + + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + uint32 version = 8; + + // attributes is a jsonb formatted field. + // @inject_tag: `gorm:"default:null"` + bytes attributes = 9; +} + +message HostSet { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}]; + + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + string catalog_id = 6; + + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + uint32 version = 7; + + // attributes is a jsonb formatted field. + // @inject_tag: `gorm:"default:null"` + bytes attributes = 8; +} + +message HostCatalogSecret { + // catalog_id is the public id of the catalog containing this secret. + // @inject_tag: `gorm:"primary_key"` + string catalog_id = 1; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // attributes is the plain-text of the attribute data. We are not storing + // this plain-text value in the database. + // @inject_tag: `gorm:"-" wrapping:"pt,secret_data"` + bytes secret = 4; + + // ct_attributes is the ciphertext of the attribute data stored in the db. + // @inject_tag: `gorm:"column:secret;not_null" wrapping:"ct,secret_data"` + bytes ct_secret = 5; + + // The key_id of the kms database key used for encrypting this entry. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string key_id = 6; +} diff --git a/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto b/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto new file mode 100644 index 000000000..75d2efc8e --- /dev/null +++ b/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the host package. +package controller.storage.plugin.host.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/plugin/host/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; +import "controller/custom_options/v1/options.proto"; + +message Plugin { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 10; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 20; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 30; + + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = {this:"Name" that: "name"}]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 50 [(custom_options.v1.mask_mapping) = {this:"Description" that: "description"}]; + + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + string scope_id = 60; + + // @inject_tag: `gorm:"default:null"` + uint32 version = 70; + + // plugin_name is required. The value must be unique across all host plugins + // @inject_tag: `gorm:"default:null"` + string plugin_name = 80; + + // id_prefix is required. The value must be unique across all host plugins + // @inject_tag: `gorm:"default:null"` + string id_prefix = 90; +} \ No newline at end of file diff --git a/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto new file mode 100644 index 000000000..8a349da46 --- /dev/null +++ b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the host package. +package controller.storage.plugin.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/plugin/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; + +// This is a typeless plugin. In practice this should never be used directly. +// This is included for testing purposes only. +message Plugin { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 10; + + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + string scope_id = 20; + + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + string name = 30; +} \ No newline at end of file diff --git a/internal/scheduler/job/additional_verification_test.go b/internal/scheduler/job/additional_verification_test.go index 420ff74f0..9e89b4d4c 100644 --- a/internal/scheduler/job/additional_verification_test.go +++ b/internal/scheduler/job/additional_verification_test.go @@ -121,7 +121,7 @@ func TestPlugin(t *testing.T) { // Create test plugin id testPluginId := "pi_test1234" - numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId}) + numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId}) require.NoError(err) assert.Equal(1, numRows) @@ -154,7 +154,7 @@ func TestPlugin(t *testing.T) { // Create test plugin id testPluginId := "pi_test1234" - numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId}) + numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId}) assert.NoError(err) assert.Equal(1, numRows) @@ -177,7 +177,7 @@ func TestPlugin(t *testing.T) { // Create test plugin id testPluginId := "pi_test1234" - numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId}) + numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId}) assert.NoError(err) assert.Equal(1, numRows) From 1237d05232038e443db6cfffde61a5490707a334 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 8 Sep 2021 16:41:41 -0700 Subject: [PATCH 06/71] Create a repository to create, lookup, and list host plugins. --- internal/db/schema/postgres_migration.gen.go | 6 +- internal/plugin/host/plugin.go | 29 +- internal/plugin/host/plugin_test.go | 46 --- internal/plugin/host/repository.go | 47 +++ internal/plugin/host/repository_plugin.go | 148 ++++++++ .../plugin/host/repository_plugin_test.go | 326 ++++++++++++++++++ internal/plugin/host/repository_test.go | 118 +++++++ internal/plugin/store/plugin.pb.go | 254 +++++++++++++- .../storage/plugin/store/v1/plugin.proto | 40 +++ sdk/pbs/plugin/host_plugin_service.pb.go | 17 +- 10 files changed, 957 insertions(+), 74 deletions(-) create mode 100644 internal/plugin/host/repository.go create mode 100644 internal/plugin/host/repository_plugin.go create mode 100644 internal/plugin/host/repository_plugin_test.go create mode 100644 internal/plugin/host/repository_test.go diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index d6373fb49..1c8e4724d 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -7059,7 +7059,7 @@ alter table wh_host_dimension check(length(trim(plugin_name)) > 0) constraint plugin_name_must_be_lowercase check(lower(trim(plugin_name)) = plugin_name) - constraint plugin_host_name_uq + constraint plugin_host_plugin_name_uq unique, id_prefix text not null constraint plugin_id_prefix_must_be_not_empty @@ -7217,7 +7217,7 @@ alter table wh_host_dimension update_time wt_timestamp, version wt_version, attributes bytea not null - constraint attributes_must_not_empty + constraint attributes_must_not_be_empty check(length(attributes) > 0), constraint host_catalog_fkey foreign key (scope_id, public_id) @@ -7289,7 +7289,7 @@ alter table wh_host_dimension update_time wt_timestamp, version wt_version, attributes bytea not null - constraint attributes_must_not_empty + constraint attributes_must_not_be_empty check(length(attributes) > 0), constraint host_plugin_set_catalog_id_name_uq unique(catalog_id, name), diff --git a/internal/plugin/host/plugin.go b/internal/plugin/host/plugin.go index ddabc58ec..ca339ec8b 100644 --- a/internal/plugin/host/plugin.go +++ b/internal/plugin/host/plugin.go @@ -1,8 +1,10 @@ package host import ( + "github.com/hashicorp/boundary/internal/oplog" "github.com/hashicorp/boundary/internal/plugin/host/store" "github.com/hashicorp/boundary/internal/types/scope" + "google.golang.org/protobuf/proto" ) // A Plugin enables additional logic to be used by boundary. @@ -14,7 +16,7 @@ type Plugin struct { // NewPlugin creates a new in memory Plugin assigned to the global scope. // Name, Description are the only allowed option. All other options are ignored. -func NewPlugin(pluginName, idPrefix string, opt ...Option) *Plugin { +func NewPlugin(pluginName string, idPrefix string, opt ...Option) *Plugin { opts := getOpts(opt...) p := &Plugin{ Plugin: &store.Plugin{ @@ -41,3 +43,28 @@ func (c *Plugin) TableName() string { func (c *Plugin) SetTableName(n string) { c.tableName = n } + +func allocPlugin() *Plugin { + return &Plugin{ + Plugin: &store.Plugin{}, + } +} + +func (c *Plugin) clone() *Plugin { + cp := proto.Clone(c.Plugin) + return &Plugin{ + Plugin: cp.(*store.Plugin), + } +} + +func newPluginMetadata(p *Plugin, op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{p.GetPublicId()}, + "resource-type": []string{"host plugin"}, + "op-type": []string{op.String()}, + } + if p.ScopeId != "" { + metadata["scope-id"] = []string{p.ScopeId} + } + return metadata +} diff --git a/internal/plugin/host/plugin_test.go b/internal/plugin/host/plugin_test.go index 01bccd8ae..0d8715e9f 100644 --- a/internal/plugin/host/plugin_test.go +++ b/internal/plugin/host/plugin_test.go @@ -18,7 +18,6 @@ func TestPlugin_Create(t *testing.T) { type args struct { pluginName string idPrefix string - semVer string opts []Option } @@ -54,51 +53,6 @@ func TestPlugin_Create(t *testing.T) { }, wantErr: true, }, - { - name: "idprefix-capitalized", - args: args{ - pluginName: "idprefixcapitalized", - idPrefix: "IdPrefixCapitalized", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "idprefixcapitalized", - IdPrefix: "IdPrefixCapitalized", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "idprefix-space", - args: args{ - pluginName: "idprefix space", - idPrefix: "idprefix space", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "idprefix space", - IdPrefix: "idprefix space", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "pluginName-capitalized", - args: args{ - pluginName: "PluginNameCapitalized", - idPrefix: "pluginnamecapitalized", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "PluginNameCapitalized", - IdPrefix: "pluginnamecapitalized", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, { name: "valid-no-options", args: args{ diff --git a/internal/plugin/host/repository.go b/internal/plugin/host/repository.go new file mode 100644 index 000000000..a8604342a --- /dev/null +++ b/internal/plugin/host/repository.go @@ -0,0 +1,47 @@ +package host + +import ( + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +// A Repository stores and retrieves the persistent types in the host +// package. It is not safe to use a repository concurrently. +type Repository struct { + reader db.Reader + writer db.Writer + kms *kms.Kms + // defaultLimit provides a default for limiting the number of results + // returned from the repo + defaultLimit int +} + +// NewRepository creates a new Repository. The returned repository should +// only be used for one transaction and it is not safe for concurrent go +// routines to access it. WithLimit option is used as a repo wide default +// limit applied to all ListX methods. +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { + const op = "static.NewRepository" + switch { + case r == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Reader") + case w == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer") + case kms == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") + } + + opts := getOpts(opt...) + if opts.withLimit == 0 { + // zero signals the boundary defaults should be used. + opts.withLimit = db.DefaultLimit + } + + return &Repository{ + reader: r, + writer: w, + kms: kms, + defaultLimit: opts.withLimit, + }, nil +} diff --git a/internal/plugin/host/repository_plugin.go b/internal/plugin/host/repository_plugin.go new file mode 100644 index 000000000..b64ec8c69 --- /dev/null +++ b/internal/plugin/host/repository_plugin.go @@ -0,0 +1,148 @@ +package host + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/types/scope" +) + +// CreatePlugin inserts p into the repository and returns a new +// Plugin containing the plugin's PublicId. p is not changed. p must +// contain a valid ScopeID. p must not contain a PublicId. The PublicId is +// generated and assigned by this method. opt is ignored. +// +// Both p.Name and p.Description are optional. If p.Name is set, it must be +// unique within p.ScopeID. +// +// Both p.CreateTime and c.UpdateTime are ignored. +func (r *Repository) CreatePlugin(ctx context.Context, p *Plugin, _ ...Option) (*Plugin, error) { + const op = "host.(Repository).CreatePlugin" + if p == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil Plugin") + } + if p.Plugin == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded Plugin") + } + if p.ScopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if p.ScopeId != scope.Global.String() { + return nil, errors.New(ctx, errors.InvalidParameter, op, "scope id is not 'global'") + } + if p.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + if p.PluginName == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin name") + } + if p.IdPrefix == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no id prefix") + } + // TODO: remove the restriction on prefix having to be the same as plugin name + if p.PluginName != p.IdPrefix { + return nil, errors.New(ctx, errors.InvalidParameter, op, "id prefix and plugin name don't match") + } + + p = p.clone() + + id, err := newPluginId() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + p.PublicId = id + + oplogWrapper, err := r.kms.GetWrapper(ctx, p.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + metadata := newPluginMetadata(p, oplog.OpType_OP_TYPE_CREATE) + + var newPlugin *Plugin + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + newPlugin = p.clone() + err := w.Create( + ctx, + newPlugin, + db.WithOplog(oplogWrapper, metadata), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", p.ScopeId, p.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", p.ScopeId))) + } + return newPlugin, nil +} + +// LookupPlugin returns the Plugin for id. Returns nil, nil if no +// Plugin is found for id. +func (r *Repository) LookupPlugin(ctx context.Context, id string, _ ...Option) (*Plugin, error) { + const op = "host.(Repository).LookupPlugin" + if id == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + c := allocPlugin() + c.PublicId = id + if err := r.reader.LookupByPublicId(ctx, c); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + } + return c, nil +} + +// LookupPluginByPluginName returns the Plugin for a given name. Returns nil, nil if no +// Plugin is found with that plugin name. +func (r *Repository) LookupPluginByPluginName(ctx context.Context, name string, _ ...Option) (*Plugin, error) { + const op = "host.(Repository).LookupPluginByPluginName" + if name == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin name") + } + p := allocPlugin() + + if err := r.reader.LookupWhere(ctx, p, "plugin_name=?", name); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", name))) + } + return p, nil +} + +// ListPlugins returns a slice of Plugins for the scope IDs. WithLimit is the only option supported. +func (r *Repository) ListPlugins(ctx context.Context, scopeIds []string, opt ...Option) ([]*Plugin, error) { + const op = "host.(Repository).ListPlugins" + if len(scopeIds) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var plugins []*Plugin + err := r.reader.SearchWhere(ctx, &plugins, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return plugins, nil +} diff --git a/internal/plugin/host/repository_plugin_test.go b/internal/plugin/host/repository_plugin_test.go new file mode 100644 index 000000000..262aaf99c --- /dev/null +++ b/internal/plugin/host/repository_plugin_test.go @@ -0,0 +1,326 @@ +package host + +import ( + "context" + "strings" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/plugin/host/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_CreatePlugin(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + iam.TestRepo(t, conn, wrapper) + kmsCache := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kmsCache) + assert.NoError(t, err) + assert.NotNil(t, repo) + + tests := []struct { + name string + in *Plugin + opts []Option + want *Plugin + wantIsErr errors.Code + }{ + { + name: "nil-plugin", + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-plugin", + in: &Plugin{}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "valid-no-options", + in: &Plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + IdPrefix: "validnooptions", + PluginName: "validnooptions", + }, + }, + want: &Plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + IdPrefix: "validnooptions", + PluginName: "validnooptions", + }, + }, + }, + { + name: "valid-with-name", + in: &Plugin{ + Plugin: &store.Plugin{ + Name: "test-name-repo", + ScopeId: scope.Global.String(), + IdPrefix: "validwithname", + PluginName: "validwithname", + }, + }, + want: &Plugin{ + Plugin: &store.Plugin{ + Name: "test-name-repo", + ScopeId: scope.Global.String(), + IdPrefix: "validwithname", + PluginName: "validwithname", + }, + }, + }, + { + name: "valid-with-description", + in: &Plugin{ + Plugin: &store.Plugin{ + Description: "test-description-repo", + ScopeId: scope.Global.String(), + IdPrefix: "validwithdescription", + PluginName: "validwithdescription", + }, + }, + want: &Plugin{ + Plugin: &store.Plugin{ + Description: "test-description-repo", + ScopeId: scope.Global.String(), + IdPrefix: "validwithdescription", + PluginName: "validwithdescription", + }, + }, + }, + { + name: "mismatching-prefix-plugin-name", + in: &Plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + IdPrefix: "foo", + PluginName: "bar", + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "non-global-scope", + in: &Plugin{ + Plugin: &store.Plugin{ + ScopeId: "o_1234567890", + IdPrefix: "foo", + PluginName: "foo", + }, + }, + wantIsErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + got, err := repo.CreatePlugin(context.Background(), tt.in, tt.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(t, err) + assert.NoError(err) + assert.Empty(tt.in.PublicId) + assert.NotNil(got) + assertPublicId(t, PluginPrefix, got.PublicId) + assert.NotSame(tt.in, got) + assert.Equal(tt.want.Name, got.Name) + assert.Equal(tt.want.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + in := &Plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + IdPrefix: "invalidduplicatenames", + PluginName: "invalidduplicatenames", + Name: "invalid-duplicate-names", + }, + } + + got, err := repo.CreatePlugin(context.Background(), in) + assert.NoError(err) + assert.NotNil(got) + assertPublicId(t, PluginPrefix, got.PublicId) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + got2, err := repo.CreatePlugin(context.Background(), in) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + }) + + t.Run("invalid-duplicate-plugin-names-prefixes", func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + in := &Plugin{ + Plugin: &store.Plugin{ + ScopeId: scope.Global.String(), + IdPrefix: "invalidduplicatepluginnames", + PluginName: "invalidduplicatepluginnames", + }, + } + + got, err := repo.CreatePlugin(context.Background(), in) + assert.NoError(err) + assert.NotNil(got) + assertPublicId(t, PluginPrefix, got.PublicId) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + got2, err := repo.CreatePlugin(context.Background(), in) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + }) +} + +func assertPublicId(t *testing.T, prefix, actual string) { + t.Helper() + assert.NotEmpty(t, actual) + parts := strings.Split(actual, "_") + assert.Equalf(t, 2, len(parts), "want one '_' in PublicId, got multiple in %q", actual) + assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) +} + +func TestRepository_LookupPlugin(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + plg := TestPlugin(t, conn, "test", "test") + badId, err := newPluginId() + assert.NoError(t, err) + assert.NotNil(t, badId) + + tests := []struct { + name string + id string + want *Plugin + wantErr errors.Code + }{ + { + name: "found", + id: plg.GetPublicId(), + want: plg, + }, + { + name: "not-found", + id: badId, + want: nil, + }, + { + name: "bad-public-id", + id: "", + want: nil, + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + + got, err := repo.LookupPlugin(context.Background(), tt.id) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(err) + + switch { + case tt.want == nil: + assert.Nil(got) + case tt.want != nil: + assert.NotNil(got) + assert.Equal(got, tt.want) + } + }) + } +} + +func TestRepository_LookupPluginByPluginName(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + plg := TestPlugin(t, conn, "name123", "name123") + + tests := []struct { + name string + pluginName string + want *Plugin + wantErr errors.Code + }{ + { + name: "found", + pluginName: plg.GetPluginName(), + want: plg, + }, + { + name: "not-found", + pluginName: "randomname", + want: nil, + }, + { + name: "emptyname", + pluginName: "", + want: nil, + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + + got, err := repo.LookupPluginByPluginName(context.Background(), tt.pluginName) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(err) + + switch { + case tt.want == nil: + assert.Nil(got) + case tt.want != nil: + assert.NotNil(got) + assert.Equal(got, tt.want) + } + }) + } +} diff --git a/internal/plugin/host/repository_test.go b/internal/plugin/host/repository_test.go new file mode 100644 index 000000000..ba4b57797 --- /dev/null +++ b/internal/plugin/host/repository_test.go @@ -0,0 +1,118 @@ +package host + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +func TestRepository_New(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + type args struct { + r db.Reader + w db.Writer + kms *kms.Kms + opts []Option + } + + tests := []struct { + name string + args args + want *Repository + wantIsErr errors.Code + }{ + { + name: "valid", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: db.DefaultLimit, + }, + }, + { + name: "valid-with-limit", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + opts: []Option{WithLimit(5)}, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: 5, + }, + }, + { + name: "nil-reader", + args: args{ + r: nil, + w: rw, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-writer", + args: args{ + r: rw, + w: nil, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-kms", + args: args{ + r: rw, + w: rw, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "all-nils", + args: args{ + r: nil, + w: nil, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + assert.NoError(err) + require.NotNil(got) + assert.Equal(tt.want, got) + }) + } +} diff --git a/internal/plugin/store/plugin.pb.go b/internal/plugin/store/plugin.pb.go index 54d39c526..3f3116aa8 100644 --- a/internal/plugin/store/plugin.pb.go +++ b/internal/plugin/store/plugin.pb.go @@ -9,7 +9,7 @@ package store import ( - _ "github.com/hashicorp/boundary/internal/db/timestamp" + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -94,6 +94,174 @@ func (x *Plugin) GetName() string { return "" } +type PluginVersion struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // plugin_id is a foreign key to the plugin table. + // @inject_tag: `gorm:"default:null"` + PluginId string `protobuf:"bytes,20,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty" gorm:"default:null"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // semantic_version is required and is the symantic version for the above plugin. + // @inject_tag: `gorm:"default:null"` + SemanticVersion string `protobuf:"bytes,4,opt,name=semantic_version,json=semanticVersion,proto3" json:"semantic_version,omitempty" gorm:"default:null"` +} + +func (x *PluginVersion) Reset() { + *x = PluginVersion{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PluginVersion) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PluginVersion) ProtoMessage() {} + +func (x *PluginVersion) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_plugin_store_v1_plugin_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 PluginVersion.ProtoReflect.Descriptor instead. +func (*PluginVersion) Descriptor() ([]byte, []int) { + return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{1} +} + +func (x *PluginVersion) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *PluginVersion) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *PluginVersion) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *PluginVersion) GetSemanticVersion() string { + if x != nil { + return x.SemanticVersion + } + return "" +} + +type PluginExecutable struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // version_id is a foreign key to the plugin version table. + // @inject_tag: `gorm:"primary_key"` + VersionId string `protobuf:"bytes,10,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty" gorm:"primary_key"` + // operating_system to the operating system this executable is built to run on. + // @inject_tag: `gorm:"primary_key"` + OperatingSystem string `protobuf:"bytes,20,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty" gorm:"primary_key"` + // architecture to the architecture this executable is built to run on. + // @inject_tag: `gorm:"primary_key"` + Architecture string `protobuf:"bytes,30,opt,name=architecture,proto3" json:"architecture,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // executable is required and is the compressed executable for this plugin. + // @inject_tag: `gorm:"default:null"` + Executable []byte `protobuf:"bytes,50,opt,name=executable,proto3" json:"executable,omitempty" gorm:"default:null"` +} + +func (x *PluginExecutable) Reset() { + *x = PluginExecutable{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PluginExecutable) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PluginExecutable) ProtoMessage() {} + +func (x *PluginExecutable) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_plugin_store_v1_plugin_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 PluginExecutable.ProtoReflect.Descriptor instead. +func (*PluginExecutable) Descriptor() ([]byte, []int) { + return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{2} +} + +func (x *PluginExecutable) GetVersionId() string { + if x != nil { + return x.VersionId + } + return "" +} + +func (x *PluginExecutable) GetOperatingSystem() string { + if x != nil { + return x.OperatingSystem + } + return "" +} + +func (x *PluginExecutable) GetArchitecture() string { + if x != nil { + return x.Architecture + } + return "" +} + +func (x *PluginExecutable) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *PluginExecutable) GetExecutable() []byte { + if x != nil { + return x.Executable + } + return nil +} + var File_controller_storage_plugin_store_v1_plugin_proto protoreflect.FileDescriptor var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{ @@ -110,12 +278,39 @@ var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x3b, 0x5a, 0x39, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc1, 0x01, 0x0a, + 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, + 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, + 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0xed, 0x01, 0x0a, 0x10, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, + 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x32, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -130,16 +325,21 @@ func file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP() []byte { return file_controller_storage_plugin_store_v1_plugin_proto_rawDescData } -var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_storage_plugin_store_v1_plugin_proto_goTypes = []interface{}{ - (*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin + (*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin + (*PluginVersion)(nil), // 1: controller.storage.plugin.store.v1.PluginVersion + (*PluginExecutable)(nil), // 2: controller.storage.plugin.store.v1.PluginExecutable + (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 3, // 0: controller.storage.plugin.store.v1.PluginVersion.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 1: controller.storage.plugin.store.v1.PluginExecutable.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 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_controller_storage_plugin_store_v1_plugin_proto_init() } @@ -160,6 +360,30 @@ func file_controller_storage_plugin_store_v1_plugin_proto_init() { return nil } } + file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PluginVersion); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PluginExecutable); 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{ @@ -167,7 +391,7 @@ func file_controller_storage_plugin_store_v1_plugin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_plugin_store_v1_plugin_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto index 8a349da46..6db0bd94e 100644 --- a/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto +++ b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto @@ -20,4 +20,44 @@ message Plugin { // name is optional. If set, it must be unique within scope_id. // @inject_tag: `gorm:"default:null"` string name = 30; +} + +message PluginVersion { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 10; + + // plugin_id is a foreign key to the plugin table. + // @inject_tag: `gorm:"default:null"` + string plugin_id = 20; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 30; + + // semantic_version is required and is the symantic version for the above plugin. + // @inject_tag: `gorm:"default:null"` + string semantic_version = 4; +} + +message PluginExecutable { + // version_id is a foreign key to the plugin version table. + // @inject_tag: `gorm:"primary_key"` + string version_id = 10; + + // operating_system to the operating system this executable is built to run on. + // @inject_tag: `gorm:"primary_key"` + string operating_system = 20; + + // architecture to the architecture this executable is built to run on. + // @inject_tag: `gorm:"primary_key"` + string architecture = 30; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 40; + + // executable is required and is the compressed executable for this plugin. + // @inject_tag: `gorm:"default:null"` + bytes executable = 50; } \ No newline at end of file diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index 40c4aed5c..e90bad60e 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -82,7 +82,7 @@ type OnCreateCatalogResponse struct { // Secret data to persist encrypted within Boundary. This should be used to // store authentication data and other necessary configuration to be used in // later hooks and calls. Returning an error from the call will cause this - // data to not be persisted. If this is nil, no changes are written. + // data to not be persisted. If this is nil, nothing is written. Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` } @@ -132,9 +132,9 @@ type OnUpdateCatalogRequest struct { // The existing state of the catalog. CurrentCatalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=current_catalog,json=currentCatalog,proto3" json:"current_catalog,omitempty"` - // The requested new state of the catalog. This field may contain - // optional secret data that may have been updated from old - // authentication data contained within the persisted state. + // The requested new state of the catalog. This field may contain optional + // secret data that may have been updated from old authentication data + // contained within the persisted state. NewCatalog *hostcatalogs.HostCatalog `protobuf:"bytes,20,opt,name=new_catalog,json=newCatalog,proto3" json:"new_catalog,omitempty"` // The existing persisted secret data. Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` @@ -198,11 +198,10 @@ type OnUpdateCatalogResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The updated secret data to persist encrypted within Boundary. - // It's important that this be returned if it existed previously, - // as the returned data overwrites the previously existing copy. If - // an error is returned, the update of the persisted data is - // aborted. If this is nil, no changes are written. + // The updated secret data to persist encrypted within Boundary. If an error + // is returned, the update of the persisted data is aborted. If this is nil, + // no changes are written. To remove all values, simply return an allocated + // but empty map. Persisted *HostCatalogPersisted `protobuf:"bytes,10,opt,name=persisted,proto3" json:"persisted,omitempty"` } From 8104ecb3d5be0da9c3ca72e192e441a22ce48dce Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 8 Sep 2021 16:47:11 -0700 Subject: [PATCH 07/71] Fix comments describing the requirements of the arguments to CreatePlugin. --- internal/plugin/host/repository_plugin.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/plugin/host/repository_plugin.go b/internal/plugin/host/repository_plugin.go index b64ec8c69..574ade970 100644 --- a/internal/plugin/host/repository_plugin.go +++ b/internal/plugin/host/repository_plugin.go @@ -16,6 +16,9 @@ import ( // contain a valid ScopeID. p must not contain a PublicId. The PublicId is // generated and assigned by this method. opt is ignored. // +// Both p.IdPrefix and p.PluginName are required and must match. The +// p.IdPrefix must contain only lower case alphanumeric characters. +// // Both p.Name and p.Description are optional. If p.Name is set, it must be // unique within p.ScopeID. // From a78f16c9693a9ac576ce32a29053042edb96e701 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 8 Sep 2021 16:58:24 -0700 Subject: [PATCH 08/71] Adding back accidentally removed tests and removing accidentally re-added proto messages. --- internal/plugin/host/plugin_test.go | 45 ++++ internal/plugin/store/plugin.pb.go | 254 ++---------------- .../storage/plugin/store/v1/plugin.proto | 40 --- 3 files changed, 60 insertions(+), 279 deletions(-) diff --git a/internal/plugin/host/plugin_test.go b/internal/plugin/host/plugin_test.go index 0d8715e9f..a70476494 100644 --- a/internal/plugin/host/plugin_test.go +++ b/internal/plugin/host/plugin_test.go @@ -53,6 +53,51 @@ func TestPlugin_Create(t *testing.T) { }, wantErr: true, }, + { + name: "idprefix-capitalized", + args: args{ + pluginName: "idprefixcapitalized", + idPrefix: "IdPrefixCapitalized", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "idprefixcapitalized", + IdPrefix: "IdPrefixCapitalized", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "idprefix-space", + args: args{ + pluginName: "idprefix space", + idPrefix: "idprefix space", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "idprefix space", + IdPrefix: "idprefix space", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, + { + name: "pluginName-capitalized", + args: args{ + pluginName: "PluginNameCapitalized", + idPrefix: "pluginnamecapitalized", + }, + want: &Plugin{ + Plugin: &store.Plugin{ + PluginName: "PluginNameCapitalized", + IdPrefix: "pluginnamecapitalized", + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + }, { name: "valid-no-options", args: args{ diff --git a/internal/plugin/store/plugin.pb.go b/internal/plugin/store/plugin.pb.go index 3f3116aa8..54d39c526 100644 --- a/internal/plugin/store/plugin.pb.go +++ b/internal/plugin/store/plugin.pb.go @@ -9,7 +9,7 @@ package store import ( - timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/internal/db/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -94,174 +94,6 @@ func (x *Plugin) GetName() string { return "" } -type PluginVersion struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // public_id is a surrogate key suitable for use in a public API. - // @inject_tag: `gorm:"primary_key"` - PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` - // plugin_id is a foreign key to the plugin table. - // @inject_tag: `gorm:"default:null"` - PluginId string `protobuf:"bytes,20,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty" gorm:"default:null"` - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - CreateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` - // semantic_version is required and is the symantic version for the above plugin. - // @inject_tag: `gorm:"default:null"` - SemanticVersion string `protobuf:"bytes,4,opt,name=semantic_version,json=semanticVersion,proto3" json:"semantic_version,omitempty" gorm:"default:null"` -} - -func (x *PluginVersion) Reset() { - *x = PluginVersion{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PluginVersion) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PluginVersion) ProtoMessage() {} - -func (x *PluginVersion) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_plugin_store_v1_plugin_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 PluginVersion.ProtoReflect.Descriptor instead. -func (*PluginVersion) Descriptor() ([]byte, []int) { - return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{1} -} - -func (x *PluginVersion) GetPublicId() string { - if x != nil { - return x.PublicId - } - return "" -} - -func (x *PluginVersion) GetPluginId() string { - if x != nil { - return x.PluginId - } - return "" -} - -func (x *PluginVersion) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *PluginVersion) GetSemanticVersion() string { - if x != nil { - return x.SemanticVersion - } - return "" -} - -type PluginExecutable struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // version_id is a foreign key to the plugin version table. - // @inject_tag: `gorm:"primary_key"` - VersionId string `protobuf:"bytes,10,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty" gorm:"primary_key"` - // operating_system to the operating system this executable is built to run on. - // @inject_tag: `gorm:"primary_key"` - OperatingSystem string `protobuf:"bytes,20,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty" gorm:"primary_key"` - // architecture to the architecture this executable is built to run on. - // @inject_tag: `gorm:"primary_key"` - Architecture string `protobuf:"bytes,30,opt,name=architecture,proto3" json:"architecture,omitempty" gorm:"primary_key"` - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` - // executable is required and is the compressed executable for this plugin. - // @inject_tag: `gorm:"default:null"` - Executable []byte `protobuf:"bytes,50,opt,name=executable,proto3" json:"executable,omitempty" gorm:"default:null"` -} - -func (x *PluginExecutable) Reset() { - *x = PluginExecutable{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PluginExecutable) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PluginExecutable) ProtoMessage() {} - -func (x *PluginExecutable) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_plugin_store_v1_plugin_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 PluginExecutable.ProtoReflect.Descriptor instead. -func (*PluginExecutable) Descriptor() ([]byte, []int) { - return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{2} -} - -func (x *PluginExecutable) GetVersionId() string { - if x != nil { - return x.VersionId - } - return "" -} - -func (x *PluginExecutable) GetOperatingSystem() string { - if x != nil { - return x.OperatingSystem - } - return "" -} - -func (x *PluginExecutable) GetArchitecture() string { - if x != nil { - return x.Architecture - } - return "" -} - -func (x *PluginExecutable) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *PluginExecutable) GetExecutable() []byte { - if x != nil { - return x.Executable - } - return nil -} - var File_controller_storage_plugin_store_v1_plugin_proto protoreflect.FileDescriptor var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{ @@ -278,39 +110,12 @@ var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc1, 0x01, 0x0a, - 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, - 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x22, 0xed, 0x01, 0x0a, 0x10, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, - 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, - 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x32, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x3b, 0x5a, 0x39, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -325,21 +130,16 @@ func file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP() []byte { return file_controller_storage_plugin_store_v1_plugin_proto_rawDescData } -var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_controller_storage_plugin_store_v1_plugin_proto_goTypes = []interface{}{ - (*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin - (*PluginVersion)(nil), // 1: controller.storage.plugin.store.v1.PluginVersion - (*PluginExecutable)(nil), // 2: controller.storage.plugin.store.v1.PluginExecutable - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin } var file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = []int32{ - 3, // 0: controller.storage.plugin.store.v1.PluginVersion.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.storage.plugin.store.v1.PluginExecutable.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 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 + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_controller_storage_plugin_store_v1_plugin_proto_init() } @@ -360,30 +160,6 @@ func file_controller_storage_plugin_store_v1_plugin_proto_init() { return nil } } - file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PluginVersion); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PluginExecutable); 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{ @@ -391,7 +167,7 @@ func file_controller_storage_plugin_store_v1_plugin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_plugin_store_v1_plugin_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 1, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto index 6db0bd94e..b58884b97 100644 --- a/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto +++ b/internal/proto/local/controller/storage/plugin/store/v1/plugin.proto @@ -21,43 +21,3 @@ message Plugin { // @inject_tag: `gorm:"default:null"` string name = 30; } - -message PluginVersion { - // public_id is a surrogate key suitable for use in a public API. - // @inject_tag: `gorm:"primary_key"` - string public_id = 10; - - // plugin_id is a foreign key to the plugin table. - // @inject_tag: `gorm:"default:null"` - string plugin_id = 20; - - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp create_time = 30; - - // semantic_version is required and is the symantic version for the above plugin. - // @inject_tag: `gorm:"default:null"` - string semantic_version = 4; -} - -message PluginExecutable { - // version_id is a foreign key to the plugin version table. - // @inject_tag: `gorm:"primary_key"` - string version_id = 10; - - // operating_system to the operating system this executable is built to run on. - // @inject_tag: `gorm:"primary_key"` - string operating_system = 20; - - // architecture to the architecture this executable is built to run on. - // @inject_tag: `gorm:"primary_key"` - string architecture = 30; - - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp create_time = 40; - - // executable is required and is the compressed executable for this plugin. - // @inject_tag: `gorm:"default:null"` - bytes executable = 50; -} \ No newline at end of file From ce26b0d981a61db43215862df3bbdd0ee217b12b Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 8 Sep 2021 16:59:54 -0700 Subject: [PATCH 09/71] merging type of 2 arguments into a single declaration. --- internal/plugin/host/plugin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/plugin/host/plugin.go b/internal/plugin/host/plugin.go index ca339ec8b..b6a205ca7 100644 --- a/internal/plugin/host/plugin.go +++ b/internal/plugin/host/plugin.go @@ -16,7 +16,7 @@ type Plugin struct { // NewPlugin creates a new in memory Plugin assigned to the global scope. // Name, Description are the only allowed option. All other options are ignored. -func NewPlugin(pluginName string, idPrefix string, opt ...Option) *Plugin { +func NewPlugin(pluginName, idPrefix string, opt ...Option) *Plugin { opts := getOpts(opt...) p := &Plugin{ Plugin: &store.Plugin{ From 1249f9668f3d465e576f6b565cb7358a85bd2599 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Thu, 9 Sep 2021 10:45:41 -0700 Subject: [PATCH 10/71] Add Plugin Host Set Repo Create, List, Read Operations (#1497) Add repo for plugin based host sets as well as tests. --- internal/auth/subtype.go | 23 +- internal/host/plugin/host_catalog.go | 6 + internal/host/plugin/host_catalog_test.go | 23 +- internal/host/plugin/host_set.go | 27 ++ internal/host/plugin/host_set_test.go | 2 +- internal/host/plugin/ids.go | 14 +- internal/host/plugin/repository.go | 47 ++ internal/host/plugin/repository_host_set.go | 131 ++++++ .../host/plugin/repository_host_set_test.go | 416 ++++++++++++++++++ internal/host/plugin/repository_test.go | 118 +++++ internal/host/plugin/testing.go | 27 +- internal/host/plugin/testing_test.go | 4 + internal/host/subtype.go | 24 +- 13 files changed, 819 insertions(+), 43 deletions(-) create mode 100644 internal/host/plugin/repository.go create mode 100644 internal/host/plugin/repository_host_set.go create mode 100644 internal/host/plugin/repository_host_set_test.go create mode 100644 internal/host/plugin/repository_test.go diff --git a/internal/auth/subtype.go b/internal/auth/subtype.go index fbcd8e4d3..43c19cefc 100644 --- a/internal/auth/subtype.go +++ b/internal/auth/subtype.go @@ -1,7 +1,7 @@ package auth import ( - "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/boundary" "github.com/hashicorp/boundary/internal/types/subtypes" ) @@ -28,32 +28,17 @@ func Register(subtype subtypes.Subtype, prefixes ...string) error { // AuthMethod contains the common methods across all the different types of auth methods. type AuthMethod interface { - GetPublicId() string - GetCreateTime() *timestamp.Timestamp - GetUpdateTime() *timestamp.Timestamp - GetName() string - GetDescription() string + boundary.Resource GetScopeId() string - GetVersion() uint32 GetIsPrimaryAuthMethod() bool } type Account interface { - GetPublicId() string - GetCreateTime() *timestamp.Timestamp - GetUpdateTime() *timestamp.Timestamp - GetName() string - GetDescription() string + boundary.Resource GetAuthMethodId() string - GetVersion() uint32 } type ManagedGroup interface { - GetPublicId() string - GetCreateTime() *timestamp.Timestamp - GetUpdateTime() *timestamp.Timestamp - GetName() string - GetDescription() string + boundary.Resource GetAuthMethodId() string - GetVersion() uint32 } diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index 878e16274..9ac49f181 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -44,6 +44,12 @@ func NewHostCatalog(ctx context.Context, pluginId, scopeId string, opt ...Option return hc, nil } +func allocHostCatalog() *HostCatalog { + return &HostCatalog{ + HostCatalog: &store.HostCatalog{}, + } +} + func (c *HostCatalog) clone() *HostCatalog { cp := proto.Clone(c.HostCatalog) return &HostCatalog{ diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go index c3244b6cd..ec45a6185 100644 --- a/internal/host/plugin/host_catalog_test.go +++ b/internal/host/plugin/host_catalog_test.go @@ -17,6 +17,7 @@ import ( ) func TestHostCatalog_Create(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -140,21 +141,21 @@ func TestHostCatalog_Create(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - got, err := NewHostCatalog(context.Background(), tt.args.pluginId, tt.args.scopeId, tt.args.opts...) + got, err := NewHostCatalog(ctx, tt.args.pluginId, tt.args.scopeId, tt.args.opts...) require.NoError(t, err) require.NotNil(t, got) assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostCatalogId() + id, err := newHostCatalogId(plg.GetIdPrefix()) assert.NoError(t, err) tt.want.PublicId = id got.PublicId = id w := db.New(conn) - err = w.Create(context.Background(), got) + err = w.Create(ctx, got) if tt.wantCreateErr { assert.Error(t, err) } else { @@ -164,7 +165,7 @@ func TestHostCatalog_Create(t *testing.T) { PublicId: id, }, } - require.NoError(t, w.LookupById(context.Background(), found)) + require.NoError(t, w.LookupById(ctx, found)) assert.Empty(t, cmp.Diff(got.HostCatalog, found.HostCatalog, protocmp.Transform()), "%q compared to %q", got.Attributes, found.Attributes) } }) @@ -181,28 +182,28 @@ func TestHostCatalog_Create_DuplicateNames(t *testing.T) { plg := host.TestPlugin(t, conn, "test1", "prefix1") plg2 := host.TestPlugin(t, conn, "test2", "prefix2") - got, err := NewHostCatalog(context.Background(), plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + got, err := NewHostCatalog(ctx, plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId() + got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) require.NoError(t, err) w.Create(ctx, got) // Can't create another resource with the same pluginName in the same scope - got.PublicId, err = newHostCatalogId() + got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) // Can't create another resource with same pluginName in same scope even for different plugin - got, err = NewHostCatalog(context.Background(), plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + got, err = NewHostCatalog(ctx, plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId() + got.PublicId, err = newHostCatalogId(plg2.GetIdPrefix()) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) // Can create another resource with same pluginName in different scope even for same plugin - got, err = NewHostCatalog(context.Background(), plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate")) + got, err = NewHostCatalog(ctx, plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId() + got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) require.NoError(t, err) assert.NoError(t, w.Create(ctx, got)) } diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index e40290bbb..a60c6f5fb 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -6,6 +6,8 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/oplog" + "google.golang.org/protobuf/proto" ) // A HostSet is a collection of hosts from the set's catalog. @@ -51,3 +53,28 @@ func (s *HostSet) TableName() string { func (s *HostSet) SetTableName(n string) { s.tableName = n } + +func allocHostSet() *HostSet { + return &HostSet{ + HostSet: &store.HostSet{}, + } +} + +func (s *HostSet) clone() *HostSet { + cp := proto.Clone(s.HostSet) + return &HostSet{ + HostSet: cp.(*store.HostSet), + } +} + +func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{s.PublicId}, + "resource-type": []string{"plugin-host-set"}, + "op-type": []string{op.String()}, + } + if s.CatalogId != "" { + metadata["catalog-id"] = []string{s.CatalogId} + } + return metadata +} diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index 7a31b75f4..a0310fb80 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -145,7 +145,7 @@ func TestHostSet_Create(t *testing.T) { assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostSetId() + id, err := newHostSetId(plg.GetIdPrefix()) assert.NoError(t, err) tt.want.PublicId = id diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go index c4d2af7e7..9de819e62 100644 --- a/internal/host/plugin/ids.go +++ b/internal/host/plugin/ids.go @@ -1,6 +1,8 @@ package plugin import ( + "fmt" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" @@ -22,18 +24,18 @@ const ( Subtype = subtypes.Subtype("plugin") ) -func newHostCatalogId() (string, error) { - // TODO: Take in a prefix portion as an argument. - id, err := db.NewPublicId(HostCatalogPrefix) +func newHostCatalogId(upre string) (string, error) { + prefix := fmt.Sprintf("%s_%s", HostCatalogPrefix, upre) + id, err := db.NewPublicId(prefix) if err != nil { return "", errors.WrapDeprecated(err, "plugin.newHostCatalogId") } return id, nil } -func newHostSetId() (string, error) { - // TODO: Take in a prefix portion as an argument. - id, err := db.NewPublicId(HostSetPrefix) +func newHostSetId(upre string) (string, error) { + prefix := fmt.Sprintf("%s_%s", HostSetPrefix, upre) + id, err := db.NewPublicId(prefix) if err != nil { return "", errors.WrapDeprecated(err, "plugin.newHostSetId") } diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go new file mode 100644 index 000000000..298885732 --- /dev/null +++ b/internal/host/plugin/repository.go @@ -0,0 +1,47 @@ +package plugin + +import ( + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +// A Repository stores and retrieves the persistent types in the plugin +// package. It is not safe to use a repository concurrently. +type Repository struct { + reader db.Reader + writer db.Writer + kms *kms.Kms + // defaultLimit provides a default for limiting the number of results + // returned from the repo + defaultLimit int +} + +// NewRepository creates a new Repository. The returned repository should +// only be used for one transaction and it is not safe for concurrent go +// routines to access it. WithLimit option is used as a repo wide default +// limit applied to all ListX methods. +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { + const op = "plugin.NewRepository" + switch { + case r == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Reader") + case w == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer") + case kms == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") + } + + opts := getOpts(opt...) + if opts.withLimit == 0 { + // zero signals the boundary defaults should be used. + opts.withLimit = db.DefaultLimit + } + + return &Repository{ + reader: r, + writer: w, + kms: kms, + defaultLimit: opts.withLimit, + }, nil +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go new file mode 100644 index 000000000..d4e0a440f --- /dev/null +++ b/internal/host/plugin/repository_host_set.go @@ -0,0 +1,131 @@ +package plugin + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" +) + +// CreateSet inserts s into the repository and returns a new HostSet +// containing the host set's PublicId. s is not changed. s must contain a +// valid CatalogId. s must not contain a PublicId. The PublicId is +// generated and assigned by this method. opt is ignored. +// +// Both s.Name and s.Description are optional. If s.Name is set, it must be +// unique within s.CatalogId. +func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, _ ...Option) (*HostSet, error) { + const op = "plugin.(Repository).CreateSet" + if s == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostSet") + } + if s.HostSet == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostSet") + } + if s.CatalogId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + } + if s.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + if scopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if s.Attributes == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") + } + s = s.clone() + + c := allocHostCatalog() + c.PublicId = s.GetCatalogId() + if err := r.reader.LookupByPublicId(ctx, c); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host catalog")) + } + + // TODO: Contain the following plugin logic inside a plugin manager as well as + // calling the plugin which we have looked up. + plg := hostplg.NewPlugin("", "") + plg.PublicId = c.GetPluginId() + if err := r.reader.LookupByPublicId(ctx, plg); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host plugin")) + } + + id, err := newHostSetId(plg.GetIdPrefix()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + s.PublicId = id + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var newHostSet *HostSet + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + newHostSet = s.clone() + err := w.Create(ctx, newHostSet, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_CREATE))) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s: name %s already exists", s.CatalogId, s.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId))) + } + return newHostSet, nil +} + +// LookupSet will look up a host set in the repository and return the host +// set. If the host set is not found, it will return nil, nil. +// All options are ignored. +func (r *Repository) LookupSet(ctx context.Context, publicId string, _ ...Option) (*HostSet, error) { + const op = "plugin.(Repository).LookupSet" + if publicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + + s := allocHostSet() + s.PublicId = publicId + + err := r.reader.LookupByPublicId(ctx, s) + if errors.IsNotFoundError(err) { + return nil, nil + } + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", s.PublicId))) + } + + return s, nil +} + +// ListSets returns a slice of HostSets for the catalogId. WithLimit is the +// only option supported. +func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...Option) ([]*HostSet, error) { + const op = "plugin.(Repository).ListSets" + if catalogId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var sets []*HostSet + err := r.reader.SearchWhere(ctx, &sets, "catalog_id = ?", []interface{}{catalogId}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return sets, nil +} diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go new file mode 100644 index 000000000..ca63bf83a --- /dev/null +++ b/internal/host/plugin/repository_host_set_test.go @@ -0,0 +1,416 @@ +package plugin + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestRepository_CreateSet(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "create", "create") + catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + attrs := []byte("{}") + + tests := []struct { + name string + in *HostSet + opts []Option + want *HostSet + wantIsErr errors.Code + }{ + { + name: "nil-HostSet", + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-HostSet", + in: &HostSet{}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "invalid-no-catalog-id", + in: &HostSet{ + HostSet: &store.HostSet{ + Attributes: attrs, + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "invalid-public-id-set", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + PublicId: "abcd_OOOOOOOOOO", + Attributes: attrs, + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "invalid-no-attribte", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "valid-no-options", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Attributes: attrs, + }, + }, + }, + { + name: "valid-with-name", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Name: "test-name-repo", + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Name: "test-name-repo", + Attributes: attrs, + }, + }, + }, + { + name: "valid-with-description", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Description: ("test-description-repo"), + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Description: ("test-description-repo"), + Attributes: attrs, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(err) + assert.Empty(tt.in.PublicId) + require.NotNil(got) + assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) + assert.NotSame(tt.in, got) + assert.Equal(tt.want.Name, got.GetName()) + assert.Equal(tt.want.Description, got.GetDescription()) + assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) + assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iamRepo) + catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + + in := &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Name: "test-name-repo", + Attributes: []byte("{}"), + }, + } + + got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + require.NoError(err) + require.NotNil(got) + assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) + assert.NotSame(in, got) + assert.Equal(in.Name, got.GetName()) + assert.Equal(in.Description, got.GetDescription()) + assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) + + got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + }) + + t.Run("valid-duplicate-names-diff-catalogs", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iamRepo) + catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + + in := &HostSet{ + HostSet: &store.HostSet{ + Name: "test-name-repo", + Attributes: []byte("{}"), + }, + } + in2 := in.clone() + + in.CatalogId = catalogA.PublicId + got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + require.NoError(err) + require.NotNil(got) + assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) + assert.NotSame(in, got) + assert.Equal(in.Name, got.GetName()) + assert.Equal(in.Description, got.GetDescription()) + assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) + + in2.CatalogId = catalogB.PublicId + got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in2) + require.NoError(err) + require.NotNil(got2) + assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) + assert.NotSame(in2, got2) + assert.Equal(in2.Name, got2.GetName()) + assert.Equal(in2.Description, got2.GetDescription()) + assert.Equal(got2.GetCreateTime(), got2.GetUpdateTime()) + }) +} + +func TestRepository_LookupSet(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "lookup", "lookup") + + catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + hostSet := TestSet(t, conn, catalog.PublicId) + hostSetId, err := newHostSetId(plg.GetIdPrefix()) + require.NoError(t, err) + + tests := []struct { + name string + in string + want *HostSet + wantIsErr errors.Code + }{ + { + name: "with-no-public-id", + wantIsErr: errors.InvalidParameter, + }, + { + name: "with-non-existing-host-set-id", + in: hostSetId, + }, + { + name: "with-existing-host-set-id", + in: hostSet.PublicId, + want: hostSet, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.LookupSet(context.Background(), tt.in) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(err) + assert.EqualValues(tt.want, got) + }) + } +} + +func TestRepository_ListSets(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "list", "list") + catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + + hostSets := []*HostSet{ + TestSet(t, conn, catalogA.PublicId), + TestSet(t, conn, catalogA.PublicId), + TestSet(t, conn, catalogA.PublicId), + } + + tests := []struct { + name string + in string + opts []Option + want []*HostSet + wantIsErr errors.Code + }{ + { + name: "with-no-catalog-id", + wantIsErr: errors.InvalidParameter, + }, + { + name: "Catalog-with-no-host-sets", + in: catalogB.PublicId, + want: []*HostSet{}, + }, + { + name: "Catalog-with-host-sets", + in: catalogA.PublicId, + want: hostSets, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.ListSets(context.Background(), tt.in, tt.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(err) + opts := []cmp.Option{ + cmpopts.SortSlices(func(x, y *HostSet) bool { return x.PublicId < y.PublicId }), + protocmp.Transform(), + } + assert.Empty(cmp.Diff(tt.want, got, opts...)) + }) + } +} + +func TestRepository_ListSets_Limits(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit") + catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + count := 10 + var hostSets []*HostSet + for i := 0; i < count; i++ { + hostSets = append(hostSets, TestSet(t, conn, catalog.PublicId)) + } + + tests := []struct { + name string + repoOpts []Option + listOpts []Option + wantLen int + }{ + { + name: "With no limits", + wantLen: count, + }, + { + name: "With repo limit", + repoOpts: []Option{WithLimit(3)}, + wantLen: 3, + }, + { + name: "With negative repo limit", + repoOpts: []Option{WithLimit(-1)}, + wantLen: count, + }, + { + name: "With List limit", + listOpts: []Option{WithLimit(3)}, + wantLen: 3, + }, + { + name: "With negative List limit", + listOpts: []Option{WithLimit(-1)}, + wantLen: count, + }, + { + name: "With repo smaller than list limit", + repoOpts: []Option{WithLimit(2)}, + listOpts: []Option{WithLimit(6)}, + wantLen: 6, + }, + { + name: "With repo larger than list limit", + repoOpts: []Option{WithLimit(6)}, + listOpts: []Option{WithLimit(2)}, + wantLen: 2, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms, tt.repoOpts...) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...) + require.NoError(err) + assert.Len(got, tt.wantLen) + }) + } +} diff --git a/internal/host/plugin/repository_test.go b/internal/host/plugin/repository_test.go new file mode 100644 index 000000000..6ef12a17e --- /dev/null +++ b/internal/host/plugin/repository_test.go @@ -0,0 +1,118 @@ +package plugin + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +func TestRepository_New(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + type args struct { + r db.Reader + w db.Writer + kms *kms.Kms + opts []Option + } + + tests := []struct { + name string + args args + want *Repository + wantIsErr errors.Code + }{ + { + name: "valid", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: db.DefaultLimit, + }, + }, + { + name: "valid-with-limit", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + opts: []Option{WithLimit(5)}, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: 5, + }, + }, + { + name: "nil-reader", + args: args{ + r: nil, + w: rw, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-writer", + args: args{ + r: rw, + w: nil, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-kms", + args: args{ + r: rw, + w: rw, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "all-nils", + args: args{ + r: nil, + w: nil, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + assert.NoError(err) + require.NotNil(got) + assert.Equal(tt.want, got) + }) + } +} diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index 9dad4eae6..86a386ebc 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/plugin/host" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -16,35 +17,51 @@ import ( func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...Option) *HostCatalog { t.Helper() ctx := context.Background() + w := db.New(conn) + cat, err := NewHostCatalog(ctx, pluginId, scopeId, opt...) require.NoError(t, err) assert.NotNil(t, cat) - id, err := newHostCatalogId() + + plg := host.NewPlugin("", "") + plg.PublicId = pluginId + require.NoError(t, w.LookupByPublicId(ctx, plg)) + + id, err := newHostCatalogId(plg.GetIdPrefix()) assert.NoError(t, err) assert.NotEmpty(t, id) cat.PublicId = id - w := db.New(conn) require.NoError(t, w.Create(ctx, cat)) return cat } -// TestSet creates count number of plugin host sets in the provided DB +// TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. func TestSet(t *testing.T, conn *gorm.DB, catalogId string, opt ...Option) *HostSet { t.Helper() ctx := context.Background() + w := db.New(conn) + assert := assert.New(t) set, err := NewHostSet(ctx, catalogId, opt...) require.NoError(t, err) assert.NotNil(set) - id, err := newHostSetId() + + cg := allocHostCatalog() + cg.PublicId = catalogId + require.NoError(t, w.LookupByPublicId(ctx, cg)) + + plg := host.NewPlugin("", "") + plg.PublicId = cg.GetPluginId() + require.NoError(t, w.LookupByPublicId(ctx, plg)) + + id, err := newHostSetId(plg.GetIdPrefix()) assert.NoError(err) assert.NotEmpty(id) set.PublicId = id - w := db.New(conn) require.NoError(t, w.Create(ctx, set)) return set } diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index fe939e159..facc56027 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -1,6 +1,8 @@ package plugin import ( + "fmt" + "strings" "testing" "github.com/hashicorp/boundary/internal/db" @@ -24,6 +26,7 @@ func Test_TestCatalogs(t *testing.T) { cs := TestCatalog(t, conn, plg.GetPublicId(), proj.GetPublicId(), WithName("foo"), WithDescription("bar")) assert.NotEmpty(cs.GetPublicId()) + assert.True(strings.HasPrefix(cs.GetPublicId(), fmt.Sprintf("%s_%s", HostCatalogPrefix, "prefix"))) assert.Equal("foo", cs.GetName()) assert.Equal("bar", cs.GetDescription()) } @@ -43,6 +46,7 @@ func Test_TestSet(t *testing.T) { c := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) set := TestSet(t, conn, c.GetPublicId(), WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) + assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix"))) assert.Equal("foo", set.GetName()) assert.Equal("bar", set.GetDescription()) } diff --git a/internal/host/subtype.go b/internal/host/subtype.go index 1508eff93..2316d0785 100644 --- a/internal/host/subtype.go +++ b/internal/host/subtype.go @@ -1,6 +1,9 @@ package host -import "github.com/hashicorp/boundary/internal/types/subtypes" +import ( + "github.com/hashicorp/boundary/internal/boundary" + "github.com/hashicorp/boundary/internal/types/subtypes" +) var registry = subtypes.NewRegistry() @@ -22,3 +25,22 @@ func SubtypeFromId(id string) subtypes.Subtype { func Register(subtype subtypes.Subtype, prefixes ...string) error { return registry.Register(subtype, prefixes...) } + +// Catalog contains the common methods across all the different types of host catalogs. +type Catalog interface { + boundary.Resource + GetScopeId() string +} + +// Set contains the common methods across all the different types of host sets. +type Set interface { + boundary.Resource + GetCatalogId() string +} + +// Host contains the common methods across all the different types of hosts. +type Host interface { + boundary.Resource + GetCatalogId() string + GetAddress() string +} From 4f09f1d74fbd9bd43ef0b267d09984dafa0f114a Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Thu, 9 Sep 2021 11:15:14 -0700 Subject: [PATCH 11/71] Add create, list, lookup functions for plugin based host catalogs (#1515) Create the repository for plugin host catalogs. A large TODO is left in the code to interface with the plugin system once ready. --- internal/host/plugin/host_catalog.go | 33 +- internal/host/plugin/host_catalog_secret.go | 38 +- .../host/plugin/host_catalog_secret_test.go | 4 +- internal/host/plugin/host_catalog_test.go | 32 +- internal/host/plugin/host_set_test.go | 6 +- internal/host/plugin/ids.go | 9 +- internal/host/plugin/immutable_fields_test.go | 4 +- internal/host/plugin/options.go | 8 + internal/host/plugin/repository.go | 3 + .../host/plugin/repository_host_catalog.go | 166 ++++++++ .../plugin/repository_host_catalog_test.go | 377 ++++++++++++++++++ internal/host/plugin/repository_host_set.go | 2 +- .../host/plugin/repository_host_set_test.go | 21 +- internal/host/plugin/testing.go | 8 +- internal/host/plugin/testing_test.go | 4 +- 15 files changed, 665 insertions(+), 50 deletions(-) create mode 100644 internal/host/plugin/repository_host_catalog.go create mode 100644 internal/host/plugin/repository_host_catalog_test.go diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index 9ac49f181..f64c5f743 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/oplog" "google.golang.org/protobuf/proto" ) @@ -17,21 +18,24 @@ import ( type HostCatalog struct { *store.HostCatalog tableName string `gorm:"-"` + + secrets map[string]interface{} `gorm:"-"` } // NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId // and pluginId. Name and description are the only valid options. All other // options are ignored. -func NewHostCatalog(ctx context.Context, pluginId, scopeId string, opt ...Option) (*HostCatalog, error) { +func NewHostCatalog(ctx context.Context, scopeId, pluginId string, opt ...Option) (*HostCatalog, error) { const op = "plugin.NewHostCatalog" opts := getOpts(opt...) hc := &HostCatalog{ HostCatalog: &store.HostCatalog{ - PluginId: pluginId, ScopeId: scopeId, + PluginId: pluginId, Name: opts.withName, Description: opts.withDescription, }, + secrets: opts.withSecrets, } if opts.withAttributes != nil { @@ -50,11 +54,22 @@ func allocHostCatalog() *HostCatalog { } } +// clone provides a deep copy of the HostCatalog with the exception of the +// secret. The secret shallow copied. func (c *HostCatalog) clone() *HostCatalog { cp := proto.Clone(c.HostCatalog) - return &HostCatalog{ + + hc := &HostCatalog{ HostCatalog: cp.(*store.HostCatalog), } + if c.secrets != nil { + newSecret := make(map[string]interface{}, len(c.secrets)) + for k, v := range c.secrets { + newSecret[k] = v + } + hc.secrets = newSecret + } + return hc } // TableName returns the table name for the host catalog. @@ -70,3 +85,15 @@ func (c *HostCatalog) TableName() string { func (c *HostCatalog) SetTableName(n string) { c.tableName = n } + +func (s *HostCatalog) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{s.PublicId}, + "resource-type": []string{"plugin-host-catalog"}, + "op-type": []string{op.String()}, + } + if s.ScopeId != "" { + metadata["scope-id"] = []string{s.ScopeId} + } + return metadata +} diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go index 74ecacd43..37b8652a3 100644 --- a/internal/host/plugin/host_catalog_secret.go +++ b/internal/host/plugin/host_catalog_secret.go @@ -4,10 +4,13 @@ import ( "context" "encoding/json" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/oplog" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/structwrapping" + "google.golang.org/protobuf/proto" ) // HostCatalogSecret contains the encrypted secret for a host catalog. @@ -19,7 +22,7 @@ type HostCatalogSecret struct { // newHostCatalogSecret creates an in memory host catalog secret. // All options are ignored. -func newHostCatalogSecret(ctx context.Context, catalogId string, attributes map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) { +func newHostCatalogSecret(ctx context.Context, catalogId string, secret map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) { const op = "plugin.newHostCatlogSecret" hcs := &HostCatalogSecret{ HostCatalogSecret: &store.HostCatalogSecret{ @@ -27,8 +30,8 @@ func newHostCatalogSecret(ctx context.Context, catalogId string, attributes map[ }, } - if attributes != nil { - attrs, err := json.Marshal(attributes) + if secret != nil { + attrs, err := json.Marshal(secret) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) } @@ -37,6 +40,19 @@ func newHostCatalogSecret(ctx context.Context, catalogId string, attributes map[ return hcs, nil } +func allocHostCatalogSecret() *HostCatalogSecret { + return &HostCatalogSecret{ + HostCatalogSecret: &store.HostCatalogSecret{}, + } +} + +func (c *HostCatalogSecret) clone() *HostCatalogSecret { + cp := proto.Clone(c.HostCatalogSecret) + return &HostCatalogSecret{ + HostCatalogSecret: cp.(*store.HostCatalogSecret), + } +} + // TableName returns the table name for the host catalog. func (c *HostCatalogSecret) TableName() string { if c.tableName != "" { @@ -90,3 +106,19 @@ func (c *HostCatalogSecret) deleteQuery() (query string, queryValues []interface } return } + +func (c *HostCatalogSecret) oplogMessage(opType db.OpType) *oplog.Message { + msg := oplog.Message{ + Message: c.clone(), + TypeName: c.TableName(), + } + switch opType { + case db.CreateOp: + msg.OpType = oplog.OpType_OP_TYPE_CREATE + case db.UpdateOp: + msg.OpType = oplog.OpType_OP_TYPE_UPDATE + case db.DeleteOp: + msg.OpType = oplog.OpType_OP_TYPE_DELETE + } + return &msg +} diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go index 243485efa..6b8364824 100644 --- a/internal/host/plugin/host_catalog_secret_test.go +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -24,7 +24,7 @@ func TestHostCatalogSecret_New(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "test") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) type args struct { catalogId string @@ -120,7 +120,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"}) diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go index ec45a6185..dac4a9a52 100644 --- a/internal/host/plugin/host_catalog_test.go +++ b/internal/host/plugin/host_catalog_test.go @@ -141,14 +141,14 @@ func TestHostCatalog_Create(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - got, err := NewHostCatalog(ctx, tt.args.pluginId, tt.args.scopeId, tt.args.opts...) + got, err := NewHostCatalog(ctx, tt.args.scopeId, tt.args.pluginId, tt.args.opts...) require.NoError(t, err) require.NotNil(t, got) assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostCatalogId(plg.GetIdPrefix()) + id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) assert.NoError(t, err) tt.want.PublicId = id @@ -182,28 +182,28 @@ func TestHostCatalog_Create_DuplicateNames(t *testing.T) { plg := host.TestPlugin(t, conn, "test1", "prefix1") plg2 := host.TestPlugin(t, conn, "test2", "prefix2") - got, err := NewHostCatalog(ctx, plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + got, err := NewHostCatalog(ctx, prj.GetPublicId(), plg.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) require.NoError(t, err) w.Create(ctx, got) - // Can't create another resource with the same pluginName in the same scope - got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) + // Can't create another resource with the same name in the same scope + got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) - // Can't create another resource with same pluginName in same scope even for different plugin - got, err = NewHostCatalog(ctx, plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate")) + // Can't create another resource with same name in same scope even for different plugin + got, err = NewHostCatalog(ctx, prj.GetPublicId(), plg2.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(plg2.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx, plg2.GetIdPrefix()) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) - // Can create another resource with same pluginName in different scope even for same plugin - got, err = NewHostCatalog(ctx, plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate")) + // Can create another resource with same name in different scope even for same plugin + got, err = NewHostCatalog(ctx, prj2.GetPublicId(), plg.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(plg.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) require.NoError(t, err) assert.NoError(t, w.Create(ctx, got)) } @@ -213,8 +213,8 @@ func TestHostCatalog_Delete(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) - ignoredCat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) + ignoredCat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) _ = ignoredCat tests := []struct { name string @@ -264,7 +264,7 @@ func TestHostCatalog_Delete_Cascading(t *testing.T) { t.Run("delete-scope", func(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "deletescope", "deletescope") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) deleted, err := w.Delete(ctx, prj) require.NoError(t, err) @@ -278,7 +278,7 @@ func TestHostCatalog_Delete_Cascading(t *testing.T) { t.Run("delete-plugin", func(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "deleteplugin", "deleteplugin") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) deleted, err := w.Delete(ctx, plg) require.NoError(t, err) diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index a0310fb80..7b2ad4129 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -17,8 +17,8 @@ func TestHostSet_Create(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) - cat2 := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) type args struct { catalogId string @@ -145,7 +145,7 @@ func TestHostSet_Create(t *testing.T) { assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostSetId(plg.GetIdPrefix()) + id, err := newHostSetId(ctx, plg.GetIdPrefix()) assert.NoError(t, err) tt.want.PublicId = id diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go index 9de819e62..cb3d91e5b 100644 --- a/internal/host/plugin/ids.go +++ b/internal/host/plugin/ids.go @@ -1,6 +1,7 @@ package plugin import ( + "context" "fmt" "github.com/hashicorp/boundary/internal/db" @@ -24,20 +25,20 @@ const ( Subtype = subtypes.Subtype("plugin") ) -func newHostCatalogId(upre string) (string, error) { +func newHostCatalogId(ctx context.Context, upre string) (string, error) { prefix := fmt.Sprintf("%s_%s", HostCatalogPrefix, upre) id, err := db.NewPublicId(prefix) if err != nil { - return "", errors.WrapDeprecated(err, "plugin.newHostCatalogId") + return "", errors.Wrap(ctx, err, "plugin.newHostCatalogId") } return id, nil } -func newHostSetId(upre string) (string, error) { +func newHostSetId(ctx context.Context, upre string) (string, error) { prefix := fmt.Sprintf("%s_%s", HostSetPrefix, upre) id, err := db.NewPublicId(prefix) if err != nil { - return "", errors.WrapDeprecated(err, "plugin.newHostSetId") + return "", errors.Wrap(ctx, err, "plugin.newHostSetId") } return id, nil } diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index 677ece4f3..0db20a72a 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -24,7 +24,7 @@ func TestPluginCatalog_ImmutableFields(t *testing.T) { o, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") plg2 := host.TestPlugin(t, conn, "test2", "prefix2") - new := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + new := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) tests := []struct { name string @@ -98,7 +98,7 @@ func TestPluginSet_ImmutableFields(t *testing.T) { ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") - cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) new := TestSet(t, conn, cat.GetPublicId()) tests := []struct { diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index df3436ba5..894ff6ef6 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -18,6 +18,7 @@ type options struct { withDescription string withLimit int withAttributes map[string]interface{} + withSecrets map[string]interface{} } func getDefaultOptions() options { @@ -49,6 +50,13 @@ func WithAttributes(attrs map[string]interface{}) Option { } } +// WithSecrets provides an optional secrets field. +func WithSecrets(secrets map[string]interface{}) Option { + return func(o *options) { + o.withSecrets = secrets + } +} + // WithLimit provides an option to provide a limit. Intentionally allowing // negative integers. If WithLimit < 0, then unlimited results are // returned. If WithLimit == 0, then default limits are used for results. diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index 298885732..5c009df96 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -1,3 +1,6 @@ +// Package plugin provides a plugin host catalog, and plugin host set resource +// which are used to interact with a host plugin as well as a repository to +// perform CRUDL and custom actions on these resources. package plugin import ( diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go new file mode 100644 index 000000000..fe6daa029 --- /dev/null +++ b/internal/host/plugin/repository_host_catalog.go @@ -0,0 +1,166 @@ +package plugin + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" +) + +// CreateCatalog inserts c into the repository and returns a new +// HostCatalog containing the catalog's PublicId. c must contain a valid +// ScopeID and PluginID. c must not contain a PublicId. The PublicId is +// generated and assigned by this method. opt is ignored. +// +// c.Secret, c.Name and c.Description are optional. If c.Name is set, it must be +// unique within c.ScopeID. If c.Secret is set, it will be stored encrypted but +// not included in the returned *HostCatalog. +// +// Both c.CreateTime and c.UpdateTime are ignored. +func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Option) (*HostCatalog, error) { + const op = "plugin.(Repository).CreateCatalog" + if c == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostCatalog") + } + if c.HostCatalog == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostCatalog") + } + if c.ScopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if c.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + if c.PluginId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin id") + } + if c.Attributes == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") + } + c = c.clone() + + // TODO: Capture this in a plugin manager and call the plugin's OnCreateCatalog function + plg := hostplg.NewPlugin("", "") + plg.PublicId = c.PluginId + if err := r.reader.LookupByPublicId(ctx, plg); err != nil { + if errors.IsNotFoundError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter), errors.WithMsg(fmt.Sprintf("can't find plugin with id: %q", c.GetPluginId()))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get host plugin with id %q", c.PluginId))) + } + + id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + c.PublicId = id + + oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } + + var hcSecret *HostCatalogSecret + if c.secrets != nil { + // TODO: Create the secret using the returned value from the call to the plugin. + hcSecret, err = newHostCatalogSecret(ctx, id, c.secrets) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hcSecret.encrypt(ctx, dbWrapper) + } + + var newHostCatalog *HostCatalog + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0, 3) + ticket, err := w.GetTicket(c) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + newHostCatalog = c.clone() + var cOplogMsg oplog.Message + if err := w.Create(ctx, newHostCatalog, db.NewOplogMsg(&cOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &cOplogMsg) + + if hcSecret != nil { + newSecret := hcSecret.clone() + q, v := newSecret.insertQuery() + rows, err := w.Exec(ctx, q, v) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rows > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 catalog secret would have been created") + } + msgs = append(msgs, newSecret.oplogMessage(db.CreateOp)) + } + + metadata := c.oplog(oplog.OpType_OP_TYPE_CREATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", c.ScopeId, c.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", c.ScopeId))) + } + return newHostCatalog, nil +} + +// LookupCatalog returns the HostCatalog for id. Returns nil, nil if no +// HostCatalog is found for id. +func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) (*HostCatalog, error) { + const op = "plugin.(Repository).LookupCatalog" + if id == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + c := allocHostCatalog() + c.PublicId = id + if err := r.reader.LookupByPublicId(ctx, c); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + } + return c, nil +} + +// ListCatalogs returns a slice of HostCatalogs for the scope IDs. WithLimit is the only option supported. +func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ...Option) ([]*HostCatalog, error) { + const op = "plugin.(Repository).ListCatalogs" + if len(scopeIds) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var hostCatalogs []*HostCatalog + err := r.reader.SearchWhere(ctx, &hostCatalogs, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return hostCatalogs, nil +} diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go new file mode 100644 index 000000000..20270c98c --- /dev/null +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -0,0 +1,377 @@ +package plugin + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_CreateCatalog(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "test", "test") + + tests := []struct { + name string + in *HostCatalog + opts []Option + want *HostCatalog + wantSecret []byte + wantIsErr errors.Code + }{ + { + name: "nil-catalog", + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-catalog", + in: &HostCatalog{}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-scope", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-plugin", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-attributes", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "valid-no-options", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + }, + { + name: "not-found-plugin", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: "unknown_plugin", + Attributes: []byte("{}"), + }, + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "valid-with-name", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Name: "test-name-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Name: "test-name-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-description", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Description: "test-description-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Description: "test-description-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + }, + { + name: "valid-with-secret", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Description: "test-description-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + secrets: map[string]interface{}{ + "k1": "v1", + "k2": 2, + "k3": nil, + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Description: "test-description-repo", + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + }, + wantSecret: []byte(`{"k1":"v1","k2":2,"k3":null}`), + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + kmsCache := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kmsCache) + assert.NoError(err) + assert.NotNil(repo) + got, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(t, err) + assert.Empty(tt.in.PublicId) + assert.NotNil(got) + assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) + assert.NotSame(tt.in, got) + assert.Equal(tt.want.Name, got.Name) + assert.Equal(tt.want.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + + cSecret := allocHostCatalogSecret() + err = rw.LookupWhere(ctx, &cSecret, "catalog_id=?", got.GetPublicId()) + if tt.wantSecret == nil { + assert.Nil(got.secrets) + require.Error(t, err) + require.True(t, errors.IsNotFoundError(err)) + return + } + require.NoError(t, err) + require.Empty(t, cSecret.Secret) + require.NotEmpty(t, cSecret.CtSecret) + + dbWrapper, err := kmsCache.GetWrapper(ctx, got.GetScopeId(), kms.KeyPurposeDatabase) + require.NoError(t, err) + cSecret.decrypt(ctx, dbWrapper) + assert.Equal(string(tt.wantSecret), string(cSecret.Secret)) + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + in := &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + Name: "test-name-repo", + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + } + + got, err := repo.CreateCatalog(context.Background(), in) + assert.NoError(err) + assert.NotNil(got) + assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + got2, err := repo.CreateCatalog(context.Background(), in) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + }) + + t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + in := &HostCatalog{ + HostCatalog: &store.HostCatalog{ + Name: "test-name-repo", + PluginId: plg.GetPublicId(), + Attributes: []byte("{}"), + }, + } + in2 := in.clone() + + in.ScopeId = prj.GetPublicId() + got, err := repo.CreateCatalog(context.Background(), in) + assert.NoError(err) + assert.NotNil(got) + assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + in2.ScopeId = org.GetPublicId() + got2, err := repo.CreateCatalog(context.Background(), in2) + assert.NoError(err) + assert.NotNil(got2) + assertPluginBasedPublicId(t, HostCatalogPrefix, got2.PublicId) + assert.NotSame(in2, got2) + assert.Equal(in2.Name, got2.Name) + assert.Equal(in2.Description, got2.Description) + assert.Equal(got2.CreateTime, got2.UpdateTime) + }) +} + +func assertPluginBasedPublicId(t *testing.T, prefix, actual string) { + t.Helper() + assert.NotEmpty(t, actual) + parts := strings.Split(actual, "_") + assert.Equalf(t, 3, len(parts), "want two '_' in PublicId, got %d in %q", len(parts), actual) + assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) +} + +func TestRepository_LookupCatalog(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "test", "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + badId, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + assert.NoError(t, err) + assert.NotNil(t, badId) + + tests := []struct { + name string + id string + want *HostCatalog + wantErr errors.Code + }{ + { + name: "found", + id: cat.GetPublicId(), + want: cat, + }, + { + name: "not-found", + id: badId, + want: nil, + }, + { + name: "bad-public-id", + id: "", + want: nil, + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(err) + assert.NotNil(repo) + + got, err := repo.LookupCatalog(ctx, tt.id) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(err) + + switch { + case tt.want == nil: + assert.Nil(got) + case tt.want != nil: + assert.NotNil(got) + assert.Equal(got, tt.want) + } + }) + } +} + +func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + assert.NoError(t, err) + assert.NotNil(t, repo) + plg := hostplg.TestPlugin(t, conn, "test", "test") + + const numPerScope = 10 + var projs []string + var total int + for i := 0; i < numPerScope; i++ { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + projs = append(projs, prj.GetPublicId()) + for j := 0; j < numPerScope; j++ { + TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + total++ + } + } + + got, err := repo.ListCatalogs(context.Background(), projs) + require.NoError(t, err) + assert.Equal(t, total, len(got)) +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index d4e0a440f..3145e7ea3 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -54,7 +54,7 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host plugin")) } - id, err := newHostSetId(plg.GetIdPrefix()) + id, err := newHostSetId(ctx, plg.GetIdPrefix()) if err != nil { return nil, errors.Wrap(ctx, err, op) } diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index ca63bf83a..bae081b41 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -28,7 +28,7 @@ func TestRepository_CreateSet(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "create", "create") - catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) attrs := []byte("{}") tests := []struct { @@ -159,7 +159,7 @@ func TestRepository_CreateSet(t *testing.T) { require.NotNil(repo) _, prj := iam.TestScopes(t, iamRepo) - catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) in := &HostSet{ HostSet: &store.HostSet{ @@ -190,8 +190,8 @@ func TestRepository_CreateSet(t *testing.T) { require.NotNil(repo) _, prj := iam.TestScopes(t, iamRepo) - catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) - catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalogA := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) in := &HostSet{ HostSet: &store.HostSet{ @@ -224,6 +224,7 @@ func TestRepository_CreateSet(t *testing.T) { } func TestRepository_LookupSet(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -232,9 +233,9 @@ func TestRepository_LookupSet(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "lookup", "lookup") - catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSet := TestSet(t, conn, catalog.PublicId) - hostSetId, err := newHostSetId(plg.GetIdPrefix()) + hostSetId, err := newHostSetId(ctx, plg.GetIdPrefix()) require.NoError(t, err) tests := []struct { @@ -265,7 +266,7 @@ func TestRepository_LookupSet(t *testing.T) { repo, err := NewRepository(rw, rw, kms) assert.NoError(err) require.NotNil(repo) - got, err := repo.LookupSet(context.Background(), tt.in) + got, err := repo.LookupSet(ctx, tt.in) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -286,8 +287,8 @@ func TestRepository_ListSets(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "list", "list") - catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) - catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalogA := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSets := []*HostSet{ TestSet(t, conn, catalogA.PublicId), @@ -350,7 +351,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit") - catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId) + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) count := 10 var hostSets []*HostSet for i := 0; i < count; i++ { diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index 86a386ebc..5b11d999f 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -14,12 +14,12 @@ import ( // TestCatalog creates count number of plugin host catalogs to the provided DB // with the provided scope id. If any errors are encountered during the creation of // the host catalog, the test will fail. -func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...Option) *HostCatalog { +func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...Option) *HostCatalog { t.Helper() ctx := context.Background() w := db.New(conn) - cat, err := NewHostCatalog(ctx, pluginId, scopeId, opt...) + cat, err := NewHostCatalog(ctx, scopeId, pluginId, opt...) require.NoError(t, err) assert.NotNil(t, cat) @@ -27,7 +27,7 @@ func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...O plg.PublicId = pluginId require.NoError(t, w.LookupByPublicId(ctx, plg)) - id, err := newHostCatalogId(plg.GetIdPrefix()) + id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) assert.NoError(t, err) assert.NotEmpty(t, id) cat.PublicId = id @@ -57,7 +57,7 @@ func TestSet(t *testing.T, conn *gorm.DB, catalogId string, opt ...Option) *Host plg.PublicId = cg.GetPluginId() require.NoError(t, w.LookupByPublicId(ctx, plg)) - id, err := newHostSetId(plg.GetIdPrefix()) + id, err := newHostSetId(ctx, plg.GetIdPrefix()) assert.NoError(err) assert.NotEmpty(id) set.PublicId = id diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index facc56027..7c11e9688 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -24,7 +24,7 @@ func Test_TestCatalogs(t *testing.T) { require.NotNil(plg) assert.NotEmpty(plg.GetPublicId()) - cs := TestCatalog(t, conn, plg.GetPublicId(), proj.GetPublicId(), WithName("foo"), WithDescription("bar")) + cs := TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId(), WithName("foo"), WithDescription("bar")) assert.NotEmpty(cs.GetPublicId()) assert.True(strings.HasPrefix(cs.GetPublicId(), fmt.Sprintf("%s_%s", HostCatalogPrefix, "prefix"))) assert.Equal("foo", cs.GetName()) @@ -43,7 +43,7 @@ func Test_TestSet(t *testing.T) { require.NotNil(plg) assert.NotEmpty(plg.GetPublicId()) - c := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId()) + c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) set := TestSet(t, conn, c.GetPublicId(), WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix"))) From 715982148f8b2fa57a535f115debc822010bf98d Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Mon, 13 Sep 2021 15:02:04 -0700 Subject: [PATCH 12/71] Create Plugin Host Set Handler for Create, List, Get (#1518) --- internal/servers/controller/common/common.go | 2 + internal/servers/controller/controller.go | 5 + internal/servers/controller/handler.go | 2 +- .../handlers/host_sets/host_set_service.go | 388 ++++++++++++---- .../host_sets/host_set_service_test.go | 439 +++++++++++++++++- 5 files changed, 734 insertions(+), 102 deletions(-) diff --git a/internal/servers/controller/common/common.go b/internal/servers/controller/common/common.go index 1c07bfa68..dfa1405ca 100644 --- a/internal/servers/controller/common/common.go +++ b/internal/servers/controller/common/common.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/credential/vault" + pluginhost "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers" @@ -19,6 +20,7 @@ type ( PasswordAuthRepoFactory func() (*password.Repository, error) ServersRepoFactory func() (*servers.Repository, error) StaticRepoFactory func() (*static.Repository, error) + PluginHostRepoFactory func() (*pluginhost.Repository, error) SessionRepoFactory func() (*session.Repository, error) TargetRepoFactory func() (*target.Repository, error) ) diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 0e389e3b2..bf04b5eb4 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -53,6 +54,7 @@ type Controller struct { ServersRepoFn common.ServersRepoFactory SessionRepoFn common.SessionRepoFactory StaticHostRepoFn common.StaticRepoFactory + PluginHostRepoFn common.PluginHostRepoFactory TargetRepoFn common.TargetRepoFactory scheduler *scheduler.Scheduler @@ -131,6 +133,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.StaticHostRepoFn = func() (*static.Repository, error) { return static.NewRepository(dbase, dbase, c.kms) } + c.PluginHostRepoFn = func() (*plugin.Repository, error) { + return plugin.NewRepository(dbase, dbase, c.kms) + } c.AuthTokenRepoFn = func() (*authtoken.Repository, error) { return authtoken.NewRepository(dbase, dbase, c.kms, authtoken.WithTokenTimeToLiveDuration(c.conf.RawConfig.Controller.AuthTokenTimeToLiveDuration), diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 0bd972914..e4dd0dbec 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -92,7 +92,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er if err := services.RegisterHostCatalogServiceHandlerServer(ctx, mux, hcs); err != nil { return nil, fmt.Errorf("failed to register host catalog service handler: %w", err) } - hss, err := host_sets.NewService(c.StaticHostRepoFn) + hss, err := host_sets.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn) if err != nil { return nil, fmt.Errorf("failed to create host set handler service: %w", err) } diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index a251efaca..84965f865 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -2,12 +2,15 @@ package host_sets import ( "context" + "encoding/json" "fmt" + "strings" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/perms" @@ -20,6 +23,7 @@ import ( pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/go-secure-stdlib/strutil" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -57,18 +61,22 @@ type Service struct { pbs.UnimplementedHostSetServiceServer staticRepoFn common.StaticRepoFactory + pluginRepoFn common.PluginHostRepoFactory } var _ pbs.HostSetServiceServer = Service{} // NewService returns a host set Service which handles host set related requests to boundary and uses the provided // repositories for storage and retrieval. -func NewService(repoFn common.StaticRepoFactory) (Service, error) { +func NewService(staticRepoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory) (Service, error) { const op = "host_sets.NewService" - if repoFn == nil { + if staticRepoFn == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static repository") } - return Service{staticRepoFn: repoFn}, nil + if pluginRepoFn == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing plugin repository") + } + return Service{staticRepoFn: staticRepoFn, pluginRepoFn: pluginRepoFn}, nil } func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest) (*pbs.ListHostSetsResponse, error) { @@ -217,7 +225,7 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques if authResults.Error != nil { return nil, authResults.Error } - hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req) if err != nil { return nil, err } @@ -373,63 +381,96 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH return &pbs.RemoveHostSetHostsResponse{Item: item}, nil } -func (s Service) getFromRepo(ctx context.Context, id string) (*static.HostSet, []*static.Host, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, nil, err +func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.Host, error) { + var hs host.Set + var hl []host.Host + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, nil, err + } + hset, hosts, err := repo.LookupSet(ctx, id) + if err != nil { + return nil, nil, err + } + if hset == nil { + return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) + } + for _, h := range hosts { + hl = append(hl, h) + } + hs = hset + case plugin.Subtype: + repo, err := s.pluginRepoFn() + if err != nil { + return nil, nil, err + } + hset, err := repo.LookupSet(ctx, id) + if err != nil { + return nil, nil, err + } + if hset == nil { + return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) + } + hs = hset } - h, m, err := repo.LookupSet(ctx, id) - if err != nil { - return nil, nil, err - } - if h == nil { - return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) - } - return h, m, nil + return hs, hl, nil } -func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (*static.HostSet, error) { +func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (host.Set, error) { const op = "host_sets.(Service).createInRepo" - var opts []static.Option - if item.GetName() != nil { - opts = append(opts, static.WithName(item.GetName().GetValue())) + if item == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing item") } - if item.GetDescription() != nil { - opts = append(opts, static.WithDescription(item.GetDescription().GetValue())) + var hSet host.Set + switch host.SubtypeFromId(catalogId) { + case static.Subtype: + h, err := toStorageStaticSet(ctx, catalogId, item) + if err != nil { + return nil, err + } + repo, err := s.staticRepoFn() + if err != nil { + return nil, err + } + out, err := repo.CreateSet(ctx, scopeId, h) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") + } + hSet = out + case plugin.Subtype: + h, err := toStoragePluginSet(ctx, catalogId, item) + repo, err := s.pluginRepoFn() + if err != nil { + return nil, err + } + out, err := repo.CreateSet(ctx, scopeId, h) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") + } + hSet = out + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized catalog type") } - h, err := static.NewHostSet(catalogId, opts...) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) - } - repo, err := s.staticRepoFn() - if err != nil { - return nil, err - } - out, err := repo.CreateSet(ctx, scopeId, h) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) - } - if out == nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") - } - return out, nil + return hSet, nil } -func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.HostSet) (*static.HostSet, []*static.Host, error) { +func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, error) { const op = "host_sets.(Service).updateInRepo" - var opts []static.Option - if desc := item.GetDescription(); desc != nil { - opts = append(opts, static.WithDescription(desc.GetValue())) - } - if name := item.GetName(); name != nil { - opts = append(opts, static.WithName(name.GetValue())) - } - h, err := static.NewHostSet(catalogId, opts...) + item := req.GetItem() + h, err := toStorageStaticSet(ctx, catalogId, item) if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update")) } - h.PublicId = id - dbMask := maskManager.Translate(mask) + h.PublicId = req.GetId() + dbMask := maskManager.Translate(req.GetUpdateMask().GetPaths()) if len(dbMask) == 0 { return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) } @@ -442,9 +483,13 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update host set")) } if rowsUpdated == 0 { - return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", id) + return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", req.GetId()) } - return out, m, nil + var hl []host.Host + for _, h := range m { + hl = append(hl, h) + } + return out, hl, nil } func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) { @@ -460,19 +505,39 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, return rows > 0, nil } -func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*static.HostSet, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, err +func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Set, error) { + const op = "host_sets.(Service).listFromRepo" + var sets []host.Set + switch host.SubtypeFromId(catalogId) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, err + } + sl, err := repo.ListSets(ctx, catalogId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + for _, a := range sl { + sets = append(sets, a) + } + case plugin.Subtype: + repo, err := s.pluginRepoFn() + if err != nil { + return nil, err + } + sl, err := repo.ListSets(ctx, catalogId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + for _, a := range sl { + sets = append(sets, a) + } } - hl, err := repo.ListSets(ctx, catalogId) - if err != nil { - return nil, err - } - return hl, nil + return sets, nil } -func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) { +func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) { const op = "host_sets.(Service).addInRepo" repo, err := s.staticRepoFn() if err != nil { @@ -489,10 +554,14 @@ func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds [ if out == nil { return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after adding hosts to it.") } - return out, m, nil + var hl []host.Host + for _, h := range m { + hl = append(hl, h) + } + return out, hl, nil } -func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) { +func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) { const op = "host_sets.(Service).setInRepo" repo, err := s.staticRepoFn() if err != nil { @@ -510,10 +579,14 @@ func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds [ if out == nil { return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after setting hosts for it.") } - return out, m, nil + var hl []host.Host + for _, h := range m { + hl = append(hl, h) + } + return out, hl, nil } -func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) { +func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) { const op = "host_sets.(Service).removeInRepo" repo, err := s.staticRepoFn() if err != nil { @@ -530,12 +603,22 @@ func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostId if out == nil { return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after removing hosts from it.") } - return out, m, nil + var hl []host.Host + for _, h := range m { + hl = append(hl, h) + } + return out, hl, nil } -func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) { +func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) { res := auth.VerifyResults{} - repo, err := s.staticRepoFn() + + staticRepo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return nil, res + } + pluginRepo, err := s.pluginRepoFn() if err != nil { res.Error = err return nil, res @@ -547,33 +630,66 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty case action.List, action.Create: parentId = id default: - set, _, err := repo.LookupSet(ctx, id) - if err != nil { - res.Error = err - return nil, res - } - if set == nil { - res.Error = handlers.NotFoundError() - return nil, res + var set host.Set + switch host.SubtypeFromId(id) { + case static.Subtype: + ss, _, err := staticRepo.LookupSet(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if ss == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + set = ss + case plugin.Subtype: + ps, err := pluginRepo.LookupSet(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if ps == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + set = ps } parentId = set.GetCatalogId() opts = append(opts, auth.WithId(id)) } - cat, err := repo.LookupCatalog(ctx, parentId) - if err != nil { - res.Error = err - return nil, res - } - if cat == nil { - res.Error = handlers.NotFoundError() - return nil, res + var cat host.Catalog + switch host.SubtypeFromId(id) { + case static.Subtype: + cs, err := staticRepo.LookupCatalog(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if cs == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + cat = cs + case plugin.Subtype: + pc, err := pluginRepo.LookupCatalog(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if pc == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + cat = pc } opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId)) return cat, auth.Verify(ctx, opts...) } -func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt ...handlers.Option) (*pb.HostSet, error) { +func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handlers.Option) (*pb.HostSet, error) { + const op = "host_set_service.toProto" opts := handlers.GetOpts(opt...) if opts.WithOutputFields == nil { return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building hostset proto") @@ -588,7 +704,16 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt out.HostCatalogId = in.GetCatalogId() } if outputFields.Has(globals.TypeField) { - out.Type = static.Subtype.String() + switch host.SubtypeFromId(in.GetPublicId()) { + case static.Subtype: + out.Type = static.Subtype.String() + case plugin.Subtype: + out.Type = "plugin" + idParts := strings.Split(in.GetPublicId(), "_") + if len(idParts) > 2 { + out.Type = idParts[1] + } + } } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { out.Description = &wrapperspb.StringValue{Value: in.GetDescription()} @@ -616,9 +741,62 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt out.HostIds = append(out.HostIds, h.GetPublicId()) } } + + if outputFields.Has(globals.AttributesField) { + switch h := in.(type) { + case *plugin.HostSet: + attrs := map[string]interface{}{} + err := json.Unmarshal(h.Attributes, &attrs) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + if len(attrs) > 0 { + out.Attributes, err = structpb.NewStruct(attrs) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + } + } + } + return &out, nil } +func toStorageStaticSet(ctx context.Context, catalogId string, item *pb.HostSet) (*static.HostSet, error) { + const op = "host_set_service.toStorageStaticSet" + var opts []static.Option + if item.GetName() != nil { + opts = append(opts, static.WithName(item.GetName().GetValue())) + } + if item.GetDescription() != nil { + opts = append(opts, static.WithDescription(item.GetDescription().GetValue())) + } + hs, err := static.NewHostSet(catalogId, opts...) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) + } + return hs, nil +} + +func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) (*plugin.HostSet, error) { + const op = "host_set_service.toStoragePluginSet" + var opts []plugin.Option + if item.GetName() != nil { + opts = append(opts, plugin.WithName(item.GetName().GetValue())) + } + if item.GetDescription() != nil { + opts = append(opts, plugin.WithDescription(item.GetDescription().GetValue())) + } + if item.GetAttributes() != nil { + opts = append(opts, plugin.WithAttributes(item.GetAttributes().AsMap())) + } + hs, err := plugin.NewHostSet(ctx, catalogId, opts...) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) + } + return hs, nil +} + // A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce // requirements on the structure of the request. They verify that: // * The path passed in is correctly formatted @@ -627,13 +805,25 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt // * The type asserted by the ID and/or field is known // * If relevant, the type derived from the id prefix matches what is claimed by the type field func validateGetRequest(req *pbs.GetHostSetRequest) error { - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix) + pluginPrefix := plugin.HostSetPrefix + idParts := strings.Split(req.GetId(), "_") + if len(idParts) > 2 && idParts[0] == plugin.HostSetPrefix { + pluginPrefix = strings.Join(idParts[:2], "_") + } + + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, pluginPrefix) } func validateCreateRequest(req *pbs.CreateHostSetRequest) error { return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix) { + pluginPrefix := plugin.HostCatalogPrefix + idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_") + if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { + pluginPrefix = strings.Join(idParts[:2], "_") + } + + if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) { badFields["host_catalog_id"] = "The field is incorrectly formatted." } switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) { @@ -641,6 +831,20 @@ func validateCreateRequest(req *pbs.CreateHostSetRequest) error { if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { badFields["type"] = "Doesn't match the parent resource's type." } + case plugin.Subtype: + // TODO: Remove this check when plugin id prefixes can differ from plugin name + if req.GetItem().GetType() == "" { + break + } + idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_") + if len(idParts) < 2 { + badFields["host_catalog_id"] = "The field is incorrectly formatted for a plugin id." + break + } + plgType := idParts[1] + if plgType != req.GetItem().GetType() { + badFields["type"] = "This type must match the type of the host catalog." + } } return badFields }) @@ -664,8 +868,14 @@ func validateDeleteRequest(req *pbs.DeleteHostSetRequest) error { } func validateListRequest(req *pbs.ListHostSetsRequest) error { + pluginPrefix := plugin.HostCatalogPrefix + idParts := strings.Split(req.GetHostCatalogId(), "_") + if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { + pluginPrefix = strings.Join(idParts[:2], "_") + } + badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix) { + if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) { badFields["host_catalog_id"] = "The field is incorrectly formatted." } if _, err := handlers.NewFilter(req.GetFilter()); err != nil { diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 298a54e17..355e709c8 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -11,9 +11,11 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" @@ -27,12 +29,13 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) var testAuthorizedActions = []string{"no-op", "read", "update", "delete", "add-hosts", "set-hosts", "remove-hosts"} -func TestGet(t *testing.T) { +func TestGet_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -49,6 +52,9 @@ func TestGet(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -105,7 +111,7 @@ func TestGet(t *testing.T) { req := proto.Clone(toMerge).(*pbs.GetHostSetRequest) proto.Merge(req, tc.req) - s, err := host_sets.NewService(repoFn) + s, err := host_sets.NewService(repoFn, pluginRepoFn) require.NoError(err, "Couldn't create a new host set service.") got, gErr := s.GetHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) @@ -122,7 +128,8 @@ func TestGet(t *testing.T) { } } -func TestList(t *testing.T) { +func TestGet_Plugin(t *testing.T) { + t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) @@ -138,6 +145,101 @@ func TestList(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + + name := "test" + plg := host.TestPlugin(t, conn, name, name) + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + hs := plugin.TestSet(t, conn, hc.GetPublicId()) + + toMerge := &pbs.GetHostSetRequest{} + + pHost := &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Id: hs.GetPublicId(), + CreatedTime: hs.CreateTime.GetTimestamp(), + UpdatedTime: hs.UpdateTime.GetTimestamp(), + Type: name, + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + AuthorizedActions: testAuthorizedActions, + } + + cases := []struct { + name string + req *pbs.GetHostSetRequest + res *pbs.GetHostSetResponse + err error + }{ + { + name: "Get an Existing Host", + req: &pbs.GetHostSetRequest{Id: hs.GetPublicId()}, + res: &pbs.GetHostSetResponse{Item: pHost}, + }, + { + name: "Get a non existing Host Set", + req: &pbs.GetHostSetRequest{Id: static.HostSetPrefix + "_DoesntExis"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Wrong id prefix", + req: &pbs.GetHostSetRequest{Id: "j_1234567890"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "space in id", + req: &pbs.GetHostSetRequest{Id: static.HostPrefix + "_1 23456789"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + req := proto.Clone(toMerge).(*pbs.GetHostSetRequest) + proto.Merge(req, tc.req) + + s, err := host_sets.NewService(repoFn, pluginRepoFn) + require.NoError(err, "Couldn't create a new host set service.") + + got, gErr := s.GetHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "GetHostSet(%+v) got error %v, wanted %v", req, gErr, tc.err) + return + } + require.NoError(gErr) + + if tc.res != nil { + tc.res.Item.Version = 1 + } + assert.Empty(cmp.Diff(got.GetItem(), tc.res.GetItem(), protocmp.Transform()), "GetHostSet(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + +func TestList_Static(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + org, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2) hc, hcNoHosts := hcs[0], hcs[1] @@ -190,7 +292,108 @@ func TestList(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := host_sets.NewService(repoFn) + s, err := host_sets.NewService(repoFn, pluginRepoFn) + require.NoError(err, "Couldn't create new host set service.") + + // Test with non-anon user + got, gErr := s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "ListHostSets(%q) got error %v, wanted %v", tc.req, gErr, tc.err) + return + } + require.NoError(gErr) + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostSets(%q) got response %q, wanted %q", tc.req, got, tc.res) + + // Test with anon user + got, gErr = s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req) + require.NoError(gErr) + assert.Len(got.Items, len(tc.res.Items)) + for _, item := range got.GetItems() { + require.Nil(item.CreatedTime) + require.Nil(item.UpdatedTime) + require.Zero(item.Version) + require.Empty(item.HostIds) + } + }) + } +} + +func TestList_Plugin(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + org, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + name := "test" + plg := host.TestPlugin(t, conn, name, name) + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + + var wantHs []*pb.HostSet + for i := 0; i < 10; i++ { + h := plugin.TestSet(t, conn, hc.GetPublicId()) + wantHs = append(wantHs, &pb.HostSet{ + Id: h.GetPublicId(), + HostCatalogId: h.GetCatalogId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + CreatedTime: h.GetCreateTime().GetTimestamp(), + UpdatedTime: h.GetUpdateTime().GetTimestamp(), + Version: h.GetVersion(), + Type: name, + AuthorizedActions: testAuthorizedActions, + }) + } + + cases := []struct { + name string + req *pbs.ListHostSetsRequest + res *pbs.ListHostSetsResponse + err error + }{ + { + name: "List Many Host Sets", + req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId()}, + res: &pbs.ListHostSetsResponse{Items: wantHs}, + }, + { + name: "List No Host Sets", + req: &pbs.ListHostSetsRequest{HostCatalogId: hcNoHosts.GetPublicId()}, + res: &pbs.ListHostSetsResponse{}, + }, + { + name: "Filter To One Host Set", + req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantHs[1].GetId())}, + res: &pbs.ListHostSetsResponse{Items: wantHs[1:2]}, + }, + { + name: "Filter To No Host Sets", + req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/name"=="doesnt match"`}, + res: &pbs.ListHostSetsResponse{}, + }, + { + name: "Filter Bad Format", + req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"//id/"=="bad"`}, + err: handlers.InvalidArgumentErrorf("bad format", nil), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + s, err := host_sets.NewService(repoFn, pluginRepoFn) require.NoError(err, "Couldn't create new host set service.") // Test with non-anon user @@ -234,10 +437,13 @@ func TestDelete(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - s, err := host_sets.NewService(repoFn) + s, err := host_sets.NewService(repoFn, pluginRepoFn) require.NoError(t, err, "Couldn't create a new host set service.") cases := []struct { @@ -310,10 +516,13 @@ func TestDelete_twice(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - s, err := host_sets.NewService(repoFn) + s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(err, "Couldn't create a new host set service.") req := &pbs.DeleteHostSetRequest{ Id: h.GetPublicId(), @@ -326,7 +535,7 @@ func TestDelete_twice(t *testing.T) { assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.") } -func TestCreate(t *testing.T) { +func TestCreate_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -343,6 +552,9 @@ func TestCreate(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime() @@ -434,7 +646,7 @@ func TestCreate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := host_sets.NewService(repoFn) + s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(err, "Failed to create a new host set service.") got, gErr := s.CreateHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) @@ -466,6 +678,197 @@ func TestCreate(t *testing.T) { } } +func TestCreate_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + org, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + name := "test" + plg := host.TestPlugin(t, conn, name, name) + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + + testAttrs, err := structpb.NewStruct(map[string]interface{}{ + "int": 1, + "zero int": 0, + "string": "foo", + "zero string": "", + "bytes": []byte("bar"), + "zero bytes": nil, + "bool": true, + "zero bool": false, + "nested": map[string]interface{}{ + "int": 1, + "zero int": 0, + "string": "foo", + "zero string": "", + "bytes": []byte("bar"), + "zero bytes": nil, + "bool": true, + "zero bool": false, + }, + }) + require.NoError(t, err) + + defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime() + + cases := []struct { + name string + req *pbs.CreateHostSetRequest + res *pbs.CreateHostSetResponse + err error + }{ + { + name: "No Attributes", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + }}, + res: &pbs.CreateHostSetResponse{ + Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "With Attributes", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + Attributes: testAttrs, + }}, + res: &pbs.CreateHostSetResponse{ + Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + AuthorizedActions: testAuthorizedActions, + Attributes: testAttrs, + }, + }, + }, + { + name: "Create with mismatched type/name", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: "ThisIsMadeUp", + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Create with no type", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "no type name"}, + Description: &wrappers.StringValue{Value: "no type desc"}, + }}, + res: &pbs.CreateHostSetResponse{ + Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Name: &wrappers.StringValue{Value: "no type name"}, + Description: &wrappers.StringValue{Value: "no type desc"}, + Type: name, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Can't specify Id", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Id: "not allowed to be set", + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Created Time", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + CreatedTime: timestamppb.Now(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Update Time", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + UpdatedTime: timestamppb.Now(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + s, err := host_sets.NewService(repoFn, plgRepoFn) + require.NoError(err, "Failed to create a new host set service.") + + got, gErr := s.CreateHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "CreateHostSet(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + return + } + require.NoError(gErr) + if got != nil { + assert.Contains(got.GetUri(), tc.res.GetUri()) + assert.True(strings.HasPrefix(got.GetItem().GetId(), plugin.HostSetPrefix), got.GetItem().GetId()) + gotCreateTime := got.GetItem().GetCreatedTime().AsTime() + require.NoError(err, "Error converting proto to timestamp.") + gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime() + require.NoError(err, "Error converting proto to timestamp") + // Verify it is a set created after the test setup's default set + assert.True(gotCreateTime.After(defaultHcCreated), "New set should have been created after default set. Was created %v, which is after %v", gotCreateTime, defaultHcCreated) + assert.True(gotUpdateTime.After(defaultHcCreated), "New set should have been updated after default set. Was updated %v, which is after %v", gotUpdateTime, defaultHcCreated) + + // Clear all values which are hard to compare against. + got.Uri, tc.res.Uri = "", "" + got.Item.Id, tc.res.Item.Id = "", "" + got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil + } + if tc.res != nil { + tc.res.Item.Version = 1 + } + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CreateHostSet(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + func TestUpdate(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") @@ -512,7 +915,10 @@ func TestUpdate(t *testing.T) { Id: hs.GetPublicId(), } - tested, err := host_sets.NewService(repoFn) + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + tested, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Failed to create a new host set service.") cases := []struct { @@ -848,7 +1254,10 @@ func TestAddHostSetHosts(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - s, err := host_sets.NewService(repoFn) + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -965,7 +1374,10 @@ func TestSetHostSetHosts(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - s, err := host_sets.NewService(repoFn) + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1078,7 +1490,10 @@ func TestRemoveHostSetHosts(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - s, err := host_sets.NewService(repoFn) + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] From eb6bab8298cd594f96a5f1ef08bc2215359a5ce6 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Mon, 13 Sep 2021 16:22:11 -0700 Subject: [PATCH 13/71] Add plugin sets to targets. (#1522) --- .../handlers/targets/target_service.go | 19 +++-- .../handlers/targets/target_service_test.go | 80 +++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index da31f23f3..db043e18f 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/perms" @@ -1709,7 +1710,8 @@ func validateAddSetsRequest(req *pbs.AddTargetHostSetsRequest) error { badFields[globals.HostSetIdsField] = "Must be non-empty." } for _, id := range req.GetHostSetIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSetIdsField] = fmt.Sprintf("Incorrectly formatted host set identifier %q.", id) break } @@ -1729,7 +1731,8 @@ func validateSetSetsRequest(req *pbs.SetTargetHostSetsRequest) error { badFields[globals.VersionField] = "Required field." } for _, id := range req.GetHostSetIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSetIdsField] = fmt.Sprintf("Incorrectly formatted host set identifier %q.", id) break } @@ -1752,7 +1755,8 @@ func validateRemoveSetsRequest(req *pbs.RemoveTargetHostSetsRequest) error { badFields[globals.HostSetIdsField] = "Must be non-empty." } for _, id := range req.GetHostSetIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSetIdsField] = fmt.Sprintf("Incorrectly formatted host set identifier %q.", id) break } @@ -1775,7 +1779,8 @@ func validateAddHostSourcesRequest(req *pbs.AddTargetHostSourcesRequest) error { badFields[globals.HostSourceIdsField] = "Must be non-empty." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break } @@ -1795,7 +1800,8 @@ func validateSetHostSourcesRequest(req *pbs.SetTargetHostSourcesRequest) error { badFields[globals.VersionField] = "Required field." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break } @@ -1818,7 +1824,8 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e badFields[globals.HostSourceIdsField] = "Must be non-empty." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) { + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break } diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 19f9ac908..b776a69b0 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -18,9 +18,11 @@ import ( "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" spbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers" @@ -979,6 +981,10 @@ func TestAddTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + addCases := []struct { name string tar *target.TcpTarget @@ -1003,6 +1009,18 @@ func TestAddTargetHostSets(t *testing.T) { addHostSets: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSets: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, + { + name: "Add plugin set on empty target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + addHostSets: []string{pluginHs.GetPublicId()}, + resultHostSets: []string{pluginHs.GetPublicId()}, + }, + { + name: "Add plugin set on populated target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + addHostSets: []string{pluginHs.GetPublicId()}, + resultHostSets: []string{hs[0].GetPublicId(), pluginHs.GetPublicId()}, + }, } for _, tc := range addCases { @@ -1095,6 +1113,10 @@ func TestSetTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + setCases := []struct { name string tar *target.TcpTarget @@ -1125,6 +1147,18 @@ func TestSetTargetHostSets(t *testing.T) { setHostSets: []string{}, resultHostSets: nil, }, + { + name: "Set plugin set on empty target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + setHostSets: []string{pluginHs.GetPublicId()}, + resultHostSets: []string{pluginHs.GetPublicId()}, + }, + { + name: "Set plugin set on populated target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + setHostSets: []string{pluginHs.GetPublicId()}, + resultHostSets: []string{pluginHs.GetPublicId()}, + }, } for _, tc := range setCases { t.Run(tc.name, func(t *testing.T) { @@ -1205,6 +1239,10 @@ func TestRemoveTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + removeCases := []struct { name string tar *target.TcpTarget @@ -1236,6 +1274,12 @@ func TestRemoveTargetHostSets(t *testing.T) { removeHosts: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, resultHosts: []string{}, }, + { + name: "Remove 1 plugin of 2 sets", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), + removeHosts: []string{pluginHs.GetPublicId()}, + resultHosts: []string{hs[0].GetPublicId()}, + }, } for _, tc := range removeCases { @@ -1333,6 +1377,10 @@ func TestAddTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + addCases := []struct { name string tar *target.TcpTarget @@ -1357,6 +1405,18 @@ func TestAddTargetHostSources(t *testing.T) { addHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, + { + name: "Add plugin set on empty target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + addHostSources: []string{pluginHs.GetPublicId()}, + resultHostSources: []string{pluginHs.GetPublicId()}, + }, + { + name: "Add plugin set on populated target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + addHostSources: []string{pluginHs.GetPublicId()}, + resultHostSources: []string{hs[0].GetPublicId(), pluginHs.GetPublicId()}, + }, } for _, tc := range addCases { @@ -1449,6 +1509,10 @@ func TestSetTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + setCases := []struct { name string tar *target.TcpTarget @@ -1467,6 +1531,12 @@ func TestSetTargetHostSources(t *testing.T) { setHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[1].GetPublicId()}, }, + { + name: "Set plugin set on populated target", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + setHostSources: []string{pluginHs.GetPublicId()}, + resultHostSources: []string{pluginHs.GetPublicId()}, + }, { name: "Set duplicate host set on populated target", tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})), @@ -1559,6 +1629,10 @@ func TestRemoveTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) + plg := host.TestPlugin(t, conn, "test", "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + removeCases := []struct { name string tar *target.TcpTarget @@ -1578,6 +1652,12 @@ func TestRemoveTargetHostSources(t *testing.T) { removeHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId()}, }, + { + name: "Remove 1 plugin set of 2 sets", + tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), + removeHostSources: []string{pluginHs.GetPublicId()}, + resultHostSources: []string{hs[0].GetPublicId()}, + }, { name: "Remove 1 duplicate set of 2 sets", tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), From e07792cf747917ee6776bfa2c359180c54876025 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 13 Sep 2021 20:26:54 -0400 Subject: [PATCH 14/71] Add endpoint preferencer (#1526) --- go.mod | 3 +- go.sum | 1 - internal/libs/endpoint/doc.go | 10 ++ internal/libs/endpoint/matcher.go | 48 +++++++++ internal/libs/endpoint/matcher_test.go | 48 +++++++++ internal/libs/endpoint/option.go | 106 ++++++++++++++++++++ internal/libs/endpoint/options_test.go | 79 +++++++++++++++ internal/libs/endpoint/preferencer.go | 76 ++++++++++++++ internal/libs/endpoint/preferencer_test.go | 110 +++++++++++++++++++++ 9 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 internal/libs/endpoint/doc.go create mode 100644 internal/libs/endpoint/matcher.go create mode 100644 internal/libs/endpoint/matcher_test.go create mode 100644 internal/libs/endpoint/option.go create mode 100644 internal/libs/endpoint/options_test.go create mode 100644 internal/libs/endpoint/preferencer.go create mode 100644 internal/libs/endpoint/preferencer_test.go diff --git a/go.mod b/go.mod index 64c175c50..d9648d719 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ replace github.com/hashicorp/boundary/sdk => ./sdk require ( github.com/armon/go-metrics v0.3.9 github.com/bufbuild/buf v0.37.0 - github.com/dhui/dktest v0.3.4 + github.com/dhui/dktest v0.3.4 // indirect github.com/fatih/color v1.12.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.1.0 @@ -68,6 +68,7 @@ require ( github.com/pires/go-proxyproto v0.5.0 github.com/pkg/errors v0.9.1 github.com/posener/complete v1.2.3 + github.com/ryanuber/go-glob v1.0.0 github.com/spf13/cobra v1.1.1 // indirect github.com/stretchr/testify v1.7.0 github.com/zalando/go-keyring v0.1.1 diff --git a/go.sum b/go.sum index 9e64763ba..021d93481 100644 --- a/go.sum +++ b/go.sum @@ -380,7 +380,6 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= diff --git a/internal/libs/endpoint/doc.go b/internal/libs/endpoint/doc.go new file mode 100644 index 000000000..74bcbd740 --- /dev/null +++ b/internal/libs/endpoint/doc.go @@ -0,0 +1,10 @@ +package endpoint + +// This package contains enpoint-related libraries. +// +// Currently, this consists only of a preference chooser that, given inputs of +// IP addresses/DNS names and a user-defined preference string, can select the +// most preferred endpoint to use. If no user-defined preference string is +// supplied, an endpoint is selected at random. Creating a preferencer will +// validate input, so calling NewPreferencer and ignoring the returned struct is +// a fine way to validate incoming preference order statements. diff --git a/internal/libs/endpoint/matcher.go b/internal/libs/endpoint/matcher.go new file mode 100644 index 000000000..4f8deb79a --- /dev/null +++ b/internal/libs/endpoint/matcher.go @@ -0,0 +1,48 @@ +package endpoint + +import ( + "net" + + "github.com/ryanuber/go-glob" +) + +// matcher is a function that given an input returns whether there is a match +type matcher interface { + Match(string) bool +} + +var ( + _ matcher = (*dnsMatcher)(nil) + _ matcher = (*cidrMatcher)(nil) +) + +// DnsMatcher is a function that given an input returns true if there is a +// globbed DNS match +type dnsMatcher struct { + pattern string +} + +// Match satisfies the matcher interface +func (m dnsMatcher) Match(in string) bool { + return glob.Glob(m.pattern, in) +} + +// cidrMatcher is a function that given an input returns true if the input is +// contained within the given net +type cidrMatcher struct { + ipNet *net.IPNet +} + +// Match satisfies the matcher interface +func (m cidrMatcher) Match(in string) bool { + // Can't be created directly since this is unexported so this should never + // actually trigger, but for safety + if m.ipNet == nil { + return false + } + ip := net.ParseIP(in) + if ip == nil { + return false + } + return m.ipNet.Contains(ip) +} diff --git a/internal/libs/endpoint/matcher_test.go b/internal/libs/endpoint/matcher_test.go new file mode 100644 index 000000000..1298a1a48 --- /dev/null +++ b/internal/libs/endpoint/matcher_test.go @@ -0,0 +1,48 @@ +package endpoint + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMatchers(t *testing.T) { + t.Parallel() + t.Run("dnsMatcherEmptyPattern", func(t *testing.T) { + d := dnsMatcher{pattern: ""} + assert.False(t, d.Match("foo")) + }) + t.Run("dnsMatcherBasic", func(t *testing.T) { + d := dnsMatcher{pattern: "foo"} + assert.True(t, d.Match("foo")) + }) + t.Run("dnsMatcherMatchingGlob", func(t *testing.T) { + d := dnsMatcher{pattern: "fo*o*"} + assert.True(t, d.Match("foweroasgdf")) + }) + t.Run("dnsMatcherNonMatchingGlob", func(t *testing.T) { + d := dnsMatcher{pattern: "fo*o*"} + assert.False(t, d.Match("bfoweroasgdf")) + }) + t.Run("cidrMatcherNilMatcher", func(t *testing.T) { + d := cidrMatcher{} + assert.False(t, d.Match("1.2.3.4")) + }) + t.Run("cidrMatcherIpv4Matcher", func(t *testing.T) { + _, net, err := net.ParseCIDR("1.2.3.4/24") + require.NoError(t, err) + d := cidrMatcher{ipNet: net} + assert.True(t, d.Match("1.2.3.4")) + assert.False(t, d.Match("1.2.4.4")) + assert.False(t, d.Match("260.234.19.3")) + }) + t.Run("cidrMatcherIpv6Matcher", func(t *testing.T) { + _, net, err := net.ParseCIDR("2001:1234::/32") + require.NoError(t, err) + d := cidrMatcher{ipNet: net} + assert.True(t, d.Match("2001:1234:3092::abcd:dead:beef:2423")) + assert.False(t, d.Match("2001:1244:3092::abcd:dead:beef:2423")) + }) +} diff --git a/internal/libs/endpoint/option.go b/internal/libs/endpoint/option.go new file mode 100644 index 000000000..bf70a8624 --- /dev/null +++ b/internal/libs/endpoint/option.go @@ -0,0 +1,106 @@ +package endpoint + +import ( + "fmt" + "net" + "strings" +) + +// getOpts iterates the inbound Options and returns a struct +func getOpts(opt ...Option) (options, error) { + opts := getDefaultOptions() + for _, o := range opt { + if o == nil { + continue + } + if err := o(&opts); err != nil { + return options{}, err + } + } + return opts, nil +} + +// Option - how Options are passed as arguments +type Option func(*options) error + +// options = how options are represented +type options struct { + withIpAddrs []string + withDnsNames []string + withMatchers []matcher +} + +func getDefaultOptions() options { + return options{} +} + +// WithIpAddrs contains IP addresses to add into the endpoint possibilities. +// If an IP cannot be parsed, this function will error. +func WithIpAddrs(with []string) Option { + return func(o *options) error { + for _, addr := range with { + ip := net.ParseIP(addr) + if ip == nil { + return fmt.Errorf("input '%s' is not parseable as an ip address", addr) + } + o.withIpAddrs = append(o.withIpAddrs, addr) + } + return nil + } +} + +// WithDnsNames contains DNS names to add into the endpoint possibilities +func WithDnsNames(with []string) Option { + return func(o *options) error { + o.withDnsNames = with + return nil + } +} + +// WithPreferenceOrder contains the preference order specification. If one of +// the preferences cannot be parsed, this function will error. Internally it +// builds up a set of matchers. +func WithPreferenceOrder(with []string) Option { + return func(o *options) error { + for _, input := range with { + var m matcher + switch { + case strings.HasPrefix(input, "cidr:"): + // Make sure ParseCIDR won't choke on a bare address + cidr := strings.TrimPrefix(input, "cidr:") + if !strings.Contains(cidr, "/") { + // See if it seems like an IPv6 address vs. IPv4; colons are + // a good way to check this + if strings.Contains(cidr, ":") { + cidr = fmt.Sprintf("%s/128", cidr) + } else { + cidr = fmt.Sprintf("%s/32", cidr) + } + } + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + return fmt.Errorf("error parsing cidr %s: %w", cidr, err) + } + m = cidrMatcher{ + ipNet: ipNet, + } + + case strings.HasPrefix(input, "dns:"): + pattern := strings.TrimPrefix(input, "dns:") + if pattern == "" { + return fmt.Errorf("empty dns pattern provided") + } + m = dnsMatcher{ + pattern: pattern, + } + + default: + return fmt.Errorf("preference string %q is not supported", input) + } + if m != nil { + o.withMatchers = append(o.withMatchers, m) + } + } + return nil + } +} diff --git a/internal/libs/endpoint/options_test.go b/internal/libs/endpoint/options_test.go new file mode 100644 index 000000000..1c87293df --- /dev/null +++ b/internal/libs/endpoint/options_test.go @@ -0,0 +1,79 @@ +package endpoint + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithNil", func(t *testing.T) { + _, err := getOpts(Option(nil)) + require.NoError(t, err) + }) + t.Run("WithDnsNames", func(t *testing.T) { + opts, err := getOpts(WithDnsNames([]string{"foo.bar", "fluebar"})) + require.NoError(t, err) + testOpts := getDefaultOptions() + testOpts.withDnsNames = []string{"foo.bar", "fluebar"} + assert.Equal(t, opts, testOpts) + }) + t.Run("WithIpAddrsBadIp", func(t *testing.T) { + _, err := getOpts(WithIpAddrs([]string{"foo.bar", "1.2.3.4"})) + require.Error(t, err) + }) + t.Run("WithIpAddrs", func(t *testing.T) { + opts, err := getOpts(WithIpAddrs([]string{"1.2.3.4", "5.6.7.8"})) + require.NoError(t, err) + testOpts := getDefaultOptions() + testOpts.withIpAddrs = []string{"1.2.3.4", "5.6.7.8"} + assert.Equal(t, opts, testOpts) + }) + t.Run("WithPreferenceOrderBadCidr", func(t *testing.T) { + _, err := getOpts(WithPreferenceOrder([]string{"cidr:15.3.25.6/33", "1.2.3.4"})) + require.Error(t, err) + }) + t.Run("WithPreferenceOrderBadDns", func(t *testing.T) { + _, err := getOpts(WithPreferenceOrder([]string{"dns:"})) + require.Error(t, err) + }) + t.Run("WithPreferenceOrderBadPref", func(t *testing.T) { + _, err := getOpts(WithPreferenceOrder([]string{"abc:15.3.25.6/33", "1.2.3.4"})) + require.Error(t, err) + }) + t.Run("WithPreferenceOrder", func(t *testing.T) { + require := require.New(t) + cidr1Str := "15.3.25.6/8" + _, net1, err := net.ParseCIDR(cidr1Str) + require.NoError(err) + dns1Str := "foo.bar" + cidr2Str := "2001::44" + _, net2, err := net.ParseCIDR(cidr2Str + "/128") + require.NoError(err) + cidr3Str := "1.2.3.4" + _, net3, err := net.ParseCIDR(cidr3Str + "/32") + require.NoError(err) + opts, err := getOpts(WithPreferenceOrder([]string{"cidr:" + cidr1Str, "dns:" + dns1Str, "cidr:" + cidr2Str, "cidr:" + cidr3Str})) + require.NoError(err) + testOpts := getDefaultOptions() + + testOpts.withMatchers = []matcher{ + cidrMatcher{ + ipNet: net1, + }, + dnsMatcher{ + pattern: dns1Str, + }, + cidrMatcher{ + ipNet: net2, + }, + cidrMatcher{ + ipNet: net3, + }, + } + assert.Equal(t, opts, testOpts) + }) +} diff --git a/internal/libs/endpoint/preferencer.go b/internal/libs/endpoint/preferencer.go new file mode 100644 index 000000000..17be0893a --- /dev/null +++ b/internal/libs/endpoint/preferencer.go @@ -0,0 +1,76 @@ +package endpoint + +import ( + "context" + "math/rand" + "time" + + "github.com/hashicorp/boundary/internal/errors" +) + +type preferencer struct { + matchers []matcher +} + +// NewPreferencer builds up a preferencer with a set of preference options. This +// can then be used with Choose to select preferences from among a set of IP +// addresses and DNS names. +// +// Supported options: WithPreferenceOrder +func NewPreferencer(ctx context.Context, opt ...Option) (*preferencer, error) { + const op = "endpoint.NewPreferencer" + opts, err := getOpts(opt...) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + pref := &preferencer{ + matchers: opts.withMatchers, + } + return pref, nil +} + +// Choose takes in IP addresses and/or DNS names and chooses an endpoint from +// among them, picking one at random if there are no preferences supplied. If +// preferences are specified but none match, the empty string is returned. +// However, if no IP addresses or DNS names are supplied, an error is returned. +// +// Supported options: WithIpAddrs, WithDnsNames +func (p *preferencer) Choose(ctx context.Context, opt ...Option) (string, error) { + const op = "endpoint.(preferencer).Choose" + opts, err := getOpts(opt...) + if err != nil { + return "", errors.Wrap(ctx, err, op) + } + if len(opts.withIpAddrs)+len(opts.withDnsNames) == 0 { + return "", errors.New(ctx, errors.InvalidParameter, op, "no ip addresses or dns names passed in") + } + + switch len(p.matchers) { + case 0: + // We have no matchers, so pick one at random + allAddrs := append(opts.withIpAddrs, opts.withDnsNames...) + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + return allAddrs[rng.Intn(len(allAddrs))], nil + + default: + for _, m := range p.matchers { + switch m.(type) { + case dnsMatcher: + for _, name := range opts.withDnsNames { + if m.Match(name) { + return name, nil + } + } + case cidrMatcher: + for _, addr := range opts.withIpAddrs { + if m.Match(addr) { + return addr, nil + } + } + } + } + // Nothing matched. Don't treat it as an error, let the calling function + // simply ignore the empty result. + return "", nil + } +} diff --git a/internal/libs/endpoint/preferencer_test.go b/internal/libs/endpoint/preferencer_test.go new file mode 100644 index 000000000..917a75aa8 --- /dev/null +++ b/internal/libs/endpoint/preferencer_test.go @@ -0,0 +1,110 @@ +package endpoint + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPreferencer(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + t.Run("badOption", func(t *testing.T) { + _, err := NewPreferencer(ctx, WithPreferenceOrder([]string{"bad:1.2.3.4"})) + assert.Error(t, err) + }) + t.Run("goodOption", func(t *testing.T) { + _, err := NewPreferencer(ctx, WithPreferenceOrder([]string{"cidr:1.2.3.4"})) + assert.NoError(t, err) + }) + t.Run("chooseBadOption", func(t *testing.T) { + p, err := NewPreferencer(ctx) + require.NoError(t, err) + _, err = p.Choose(ctx, WithIpAddrs([]string{"266.1.2.3"})) + assert.Error(t, err) + }) + t.Run("noAddresses", func(t *testing.T) { + p, err := NewPreferencer(ctx) + require.NoError(t, err) + _, err = p.Choose(ctx) + assert.Error(t, err) + }) + t.Run("preferenceOrder", func(t *testing.T) { + p, err := NewPreferencer(ctx, + WithPreferenceOrder([]string{ + "cidr:1.2.3.4/24", + "dns:*.example.com", + "cidr:5.6.7.8/8", + "dns:*.company.com", + })) + require.NoError(t, err) + + cases := []struct { + name string + withIpAddrs []string + withDnsNames []string + expectedEndpoint string + expectedErrorContains string + }{ + { + name: "first cidr", + withIpAddrs: []string{"5.6.7.8", "1.2.3.56"}, + withDnsNames: []string{"foo.bar", "bar.baz"}, + expectedEndpoint: "1.2.3.56", + }, + { + name: "first dns", + withIpAddrs: []string{"48.134.5.1", "1.2.7.56"}, + withDnsNames: []string{"bar.baz", "foo.example.com"}, + expectedEndpoint: "foo.example.com", + }, + { + name: "second cidr", + withIpAddrs: []string{"5.6.7.8", "1.2.7.56"}, + withDnsNames: []string{"foo.bar", "bar.baz"}, + expectedEndpoint: "5.6.7.8", + }, + { + name: "second dns", + withIpAddrs: []string{"48.134.5.1", "1.2.7.56"}, + withDnsNames: []string{"foo.bar.com", "bar.company.com"}, + expectedEndpoint: "bar.company.com", + }, + { + name: "no match", + withIpAddrs: []string{"48.134.5.1", "1.2.7.56"}, + withDnsNames: []string{"foo.bar.com", "bar.baz.com"}, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + out, err := p.Choose(ctx, WithIpAddrs(tc.withIpAddrs), WithDnsNames(tc.withDnsNames)) + if tc.expectedErrorContains != "" { + require.Error(t, err) + assert.Contains(t, tc.expectedErrorContains, err.Error()) + } + assert.Equal(t, tc.expectedEndpoint, out) + }) + } + }) + t.Run("noPrefRandom", func(t *testing.T) { + p, err := NewPreferencer(ctx) + require.NoError(t, err) + checkMap := map[string]int{} + for i := 0; i < 200; i++ { + out, err := p.Choose( + ctx, + WithIpAddrs([]string{"48.134.5.1", "1.2.7.56"}), + WithDnsNames([]string{"foo.bar.com", "bar.baz.com"}), + ) + require.NoError(t, err) + checkMap[out] = checkMap[out] + 1 + } + // Ensure that we've inserted all four keys + assert.Len(t, checkMap, 4) + }) +} From f2236482ad7e52b10fc0f022fa9ede92c9170039 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Tue, 14 Sep 2021 09:57:07 -0700 Subject: [PATCH 15/71] Creating plugin catalog handler (#1521) The handler only supports create, lookup, and list for plugin based host catalogs. --- internal/servers/controller/common/common.go | 2 + internal/servers/controller/controller.go | 5 + internal/servers/controller/handler.go | 2 +- .../host_catalogs/host_catalog_service.go | 286 +++++++++--- .../host_catalog_service_test.go | 442 ++++++++++++++++-- 5 files changed, 642 insertions(+), 95 deletions(-) diff --git a/internal/servers/controller/common/common.go b/internal/servers/controller/common/common.go index dfa1405ca..78a9f56eb 100644 --- a/internal/servers/controller/common/common.go +++ b/internal/servers/controller/common/common.go @@ -7,6 +7,7 @@ import ( pluginhost "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" @@ -21,6 +22,7 @@ type ( ServersRepoFactory func() (*servers.Repository, error) StaticRepoFactory func() (*static.Repository, error) PluginHostRepoFactory func() (*pluginhost.Repository, error) + HostPluginRepoFactory func() (*hostplugin.Repository, error) SessionRepoFactory func() (*session.Repository, error) TargetRepoFactory func() (*target.Repository, error) ) diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index bf04b5eb4..48941bc30 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/scheduler/job" "github.com/hashicorp/boundary/internal/servers" @@ -55,6 +56,7 @@ type Controller struct { SessionRepoFn common.SessionRepoFactory StaticHostRepoFn common.StaticRepoFactory PluginHostRepoFn common.PluginHostRepoFactory + HostPluginRepoFn common.HostPluginRepoFactory TargetRepoFn common.TargetRepoFactory scheduler *scheduler.Scheduler @@ -136,6 +138,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.PluginHostRepoFn = func() (*plugin.Repository, error) { return plugin.NewRepository(dbase, dbase, c.kms) } + c.HostPluginRepoFn = func() (*host.Repository, error) { + return host.NewRepository(dbase, dbase, c.kms) + } c.AuthTokenRepoFn = func() (*authtoken.Repository, error) { return authtoken.NewRepository(dbase, dbase, c.kms, authtoken.WithTokenTimeToLiveDuration(c.conf.RawConfig.Controller.AuthTokenTimeToLiveDuration), diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index e4dd0dbec..94e259d6c 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -85,7 +85,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er runtime.WithErrorHandler(handlers.ErrorHandler()), runtime.WithForwardResponseOption(handlers.OutgoingInterceptor), ) - hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.IamRepoFn) + hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn, c.HostPluginRepoFn, c.IamRepoFn) if err != nil { return nil, fmt.Errorf("failed to create host catalog handler service: %w", err) } diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index a6e85d9e1..7edd7a1e7 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -2,12 +2,15 @@ package host_catalogs import ( "context" + "encoding/json" "fmt" + "strings" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/perms" @@ -23,6 +26,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -61,23 +65,31 @@ func init() { type Service struct { pbs.UnimplementedHostCatalogServiceServer - staticRepoFn common.StaticRepoFactory - iamRepoFn common.IamRepoFactory + staticRepoFn common.StaticRepoFactory + pluginHostRepoFn common.PluginHostRepoFactory + pluginRepoFn common.HostPluginRepoFactory + iamRepoFn common.IamRepoFactory } var _ pbs.HostCatalogServiceServer = Service{} // NewService returns a host catalog Service which handles host catalog related requests to boundary and uses the provided // repositories for storage and retrieval. -func NewService(repoFn common.StaticRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { +func NewService(repoFn common.StaticRepoFactory, pluginHostRepoFn common.PluginHostRepoFactory, hostPluginRepoFn common.HostPluginRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { const op = "host_catalogs.NewService" if repoFn == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static repository") } + if pluginHostRepoFn == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing plugin host repository") + } + if hostPluginRepoFn == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing host plugin repository") + } if iamRepoFn == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository") } - return Service{staticRepoFn: repoFn, iamRepoFn: iamRepoFn}, nil + return Service{staticRepoFn: repoFn, pluginHostRepoFn: pluginHostRepoFn, pluginRepoFn: hostPluginRepoFn, iamRepoFn: iamRepoFn}, nil } func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogsRequest) (*pbs.ListHostCatalogsResponse, error) { @@ -315,22 +327,40 @@ func (s Service) DeleteHostCatalog(ctx context.Context, req *pbs.DeleteHostCatal return nil, nil } -func (s Service) getFromRepo(ctx context.Context, id string) (*static.HostCatalog, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, err +func (s Service) getFromRepo(ctx context.Context, id string) (host.Catalog, error) { + var cat host.Catalog + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, err + } + hc, err := repo.LookupCatalog(ctx, id) + if err != nil { + return nil, err + } + if hc == nil { + return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) + } + cat = hc + case plugin.Subtype: + repo, err := s.pluginHostRepoFn() + if err != nil { + return nil, err + } + hc, err := repo.LookupCatalog(ctx, id) + if err != nil { + return nil, err + } + if hc == nil { + return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) + } + cat = hc } - hc, err := repo.LookupCatalog(ctx, id) - if err != nil { - return nil, err - } - if hc == nil { - return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) - } - return hc, nil + return cat, nil } -func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*static.HostCatalog, error) { +func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]host.Catalog, error) { repo, err := s.staticRepoFn() if err != nil { return nil, err @@ -339,21 +369,29 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*static if err != nil { return nil, err } - return ul, nil + var res []host.Catalog + for _, c := range ul { + res = append(res, c) + } + pluginRepo, err := s.pluginHostRepoFn() + if err != nil { + return nil, err + } + pl, err := pluginRepo.ListCatalogs(ctx, scopeIds) + if err != nil { + return nil, err + } + for _, c := range pl { + res = append(res, c) + } + return res, nil } -func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) { +func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) { const op = "host_catalogs.(Service).createInRepo" - var opts []static.Option - if item.GetName() != nil { - opts = append(opts, static.WithName(item.GetName().GetValue())) - } - if item.GetDescription() != nil { - opts = append(opts, static.WithDescription(item.GetDescription().GetValue())) - } - h, err := static.NewHostCatalog(projId, opts...) + h, err := toStorageStaticCatalog(ctx, projId, item) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build host catalog for creation")) + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build catalog for creation")) } repo, err := s.staticRepoFn() if err != nil { @@ -369,20 +407,51 @@ func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostC return out, nil } +func (s Service) createPluginInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*plugin.HostCatalog, error) { + const op = "host_catalogs.(Service).createInRepo" + plgRepo, err := s.pluginRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + plg, err := plgRepo.LookupPluginByPluginName(ctx, item.GetType()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + h, err := toStoragePluginCatalog(ctx, projId, plg.GetPublicId(), item) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build catalog for creation")) + } + repo, err := s.pluginHostRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, err := repo.CreateCatalog(ctx, h) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host catalog")) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host catalog but no error returned from repository.") + } + return out, nil +} + +func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (hc host.Catalog, err error) { + switch host.SubtypeFromType(item.GetType()) { + case static.Subtype: + hc, err = s.createStaticInRepo(ctx, projId, item) + default: + hc, err = s.createPluginInRepo(ctx, projId, item) + } + return hc, err +} + func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*static.HostCatalog, error) { const op = "host_catalogs.(Service).updateInRepo" - var opts []static.Option - if desc := item.GetDescription(); desc != nil { - opts = append(opts, static.WithDescription(desc.GetValue())) - } - if name := item.GetName(); name != nil { - opts = append(opts, static.WithName(name.GetValue())) - } - version := item.GetVersion() - h, err := static.NewHostCatalog(projId, opts...) + h, err := toStorageStaticCatalog(ctx, projId, item) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build host catalog for update")) } + version := item.GetVersion() h.PublicId = id dbMask := maskManager.Translate(mask) if len(dbMask) == 0 { @@ -438,28 +507,49 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. return res } default: - repo, err := s.staticRepoFn() - if err != nil { - res.Error = err - return res + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return res + } + cat, err := repo.LookupCatalog(ctx, id) + if err != nil { + res.Error = err + return res + } + if cat == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = cat.GetScopeId() + opts = append(opts, auth.WithId(id)) + case plugin.Subtype: + repo, err := s.pluginHostRepoFn() + if err != nil { + res.Error = err + return res + } + cat, err := repo.LookupCatalog(ctx, id) + if err != nil { + res.Error = err + return res + } + if cat == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = cat.GetScopeId() + opts = append(opts, auth.WithId(id)) } - cat, err := repo.LookupCatalog(ctx, id) - if err != nil { - res.Error = err - return res - } - if cat == nil { - res.Error = handlers.NotFoundError() - return res - } - parentId = cat.GetScopeId() - opts = append(opts, auth.WithId(id)) } opts = append(opts, auth.WithScopeId(parentId)) return auth.Verify(ctx, opts...) } -func toProto(ctx context.Context, in *static.HostCatalog, opt ...handlers.Option) (*pb.HostCatalog, error) { +func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb.HostCatalog, error) { + const op = "host_catalog_service.toProto" opts := handlers.GetOpts(opt...) if opts.WithOutputFields == nil { return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building auth method proto") @@ -474,7 +564,17 @@ func toProto(ctx context.Context, in *static.HostCatalog, opt ...handlers.Option out.ScopeId = in.GetScopeId() } if outputFields.Has(globals.TypeField) { - out.Type = static.Subtype.String() + switch host.SubtypeFromId(in.GetPublicId()) { + case static.Subtype: + out.Type = static.Subtype.String() + default: + // TODO: When id prefix and plugin name can diverge, update this logic. + out.Type = "plugin" + idParts := strings.Split(in.GetPublicId(), "_") + if len(idParts) > 2 { + out.Type = idParts[1] + } + } } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { out.Description = wrapperspb.String(in.GetDescription()) @@ -500,9 +600,63 @@ func toProto(ctx context.Context, in *static.HostCatalog, opt ...handlers.Option if outputFields.Has(globals.AuthorizedCollectionActionsField) { out.AuthorizedCollectionActions = opts.WithAuthorizedCollectionActions } + if outputFields.Has(globals.AttributesField) { + switch h := in.(type) { + case *plugin.HostCatalog: + attrs := map[string]interface{}{} + err := json.Unmarshal(h.Attributes, &attrs) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + if len(attrs) > 0 { + out.Attributes, err = structpb.NewStruct(attrs) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + } + } + } return &out, nil } +func toStorageStaticCatalog(ctx context.Context, scopeId string, item *pb.HostCatalog) (*static.HostCatalog, error) { + const op = "host_catalog_service.toStorageStaticCatalog" + var opts []static.Option + if name := item.GetName(); name != nil { + opts = append(opts, static.WithName(name.GetValue())) + } + if desc := item.GetDescription(); desc != nil { + opts = append(opts, static.WithDescription(desc.GetValue())) + } + hc, err := static.NewHostCatalog(scopeId, opts...) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build host catalog")) + } + return hc, nil +} + +func toStoragePluginCatalog(ctx context.Context, scopeId, plgId string, item *pb.HostCatalog) (*plugin.HostCatalog, error) { + const op = "host_catalog_service.toStoragePluginCatalog" + var opts []plugin.Option + if name := item.GetName(); name != nil { + opts = append(opts, plugin.WithName(item.GetName().GetValue())) + } + if desc := item.GetDescription(); desc != nil { + opts = append(opts, plugin.WithDescription(desc.GetValue())) + } + if attrs := item.GetAttributes(); attrs != nil { + opts = append(opts, plugin.WithAttributes(attrs.AsMap())) + } + if secrets := item.GetSecrets(); secrets != nil { + opts = append(opts, plugin.WithSecrets(secrets.AsMap())) + } + hc, err := plugin.NewHostCatalog(ctx, scopeId, plgId, opts...) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) + } + return hc, nil +} + // A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce // requirements on the structure of the request. They verify that: // * The path passed in is correctly formatted @@ -511,19 +665,23 @@ func toProto(ctx context.Context, in *static.HostCatalog, opt ...handlers.Option // * The type asserted by the ID and/or field is known // * If relevant, the type derived from the id prefix matches what is claimed by the type field func validateGetRequest(req *pbs.GetHostCatalogRequest) error { - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix) + pluginPrefix := plugin.HostCatalogPrefix + idParts := strings.Split(req.GetId(), "_") + if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { + pluginPrefix = strings.Join(idParts[:2], "_") + } + + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, pluginPrefix) } func validateCreateRequest(req *pbs.CreateHostCatalogRequest) error { return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetItem().GetScopeId()), scope.Project.Prefix()) { - badFields["scope_id"] = "This field must be a valid project scope id." + badFields[globals.ScopeIdField] = "This field must be a valid project scope id." } - switch host.SubtypeFromType(req.GetItem().GetType()) { - case static.Subtype: - default: - badFields["type"] = fmt.Sprintf("This is a required field and must be %q.", static.Subtype.String()) + if req.GetItem().GetType() == "" { + badFields[globals.TypeField] = "This field is required." } return badFields }) @@ -543,7 +701,13 @@ func validateUpdateRequest(req *pbs.UpdateHostCatalogRequest) error { } func validateDeleteRequest(req *pbs.DeleteHostCatalogRequest) error { - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix) + pluginPrefix := plugin.HostCatalogPrefix + idParts := strings.Split(req.GetId(), "_") + if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { + pluginPrefix = strings.Join(idParts[:2], "_") + } + + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, pluginPrefix) } func validateListRequest(req *pbs.ListHostCatalogsRequest) error { diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 239747a5a..d4ece68b9 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "sort" "strings" "testing" @@ -11,11 +12,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers/controller/auth" - "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/boundary/internal/types/scope" @@ -49,36 +51,27 @@ var authorizedCollectionActions = map[string]*structpb.ListValue{ var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} -func createDefaultHostCatalogAndRepo(t *testing.T) (*static.HostCatalog, *iam.Scope, common.StaticRepoFactory, common.IamRepoFactory) { - t.Helper() - require := require.New(t) +func TestGet_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) - - _, pRes := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) rw := db.New(conn) - repoFn := func() (*static.Repository, error) { + repo := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } iamRepoFn := func() (*iam.Repository, error) { return iam.TestRepo(t, conn, wrapper), nil } + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] - hc, err := static.NewHostCatalog(pRes.GetPublicId(), static.WithName("default"), static.WithDescription("default")) - require.NoError(err, "Couldn't get new catalog.") - repo, err := repoFn() - require.NoError(err, "Couldn't create static repostitory") - hcRes, err := repo.CreateCatalog(context.Background(), hc) - require.NoError(err, "Couldn't persist new catalog.") - - return hcRes, pRes, repoFn, iamRepoFn -} - -func TestGet(t *testing.T) { - t.Parallel() - hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) toMerge := &pbs.GetHostCatalogRequest{ Id: hc.GetPublicId(), } @@ -87,8 +80,6 @@ func TestGet(t *testing.T) { Id: hc.GetPublicId(), ScopeId: hc.GetScopeId(), Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, - Name: &wrappers.StringValue{Value: hc.GetName()}, - Description: &wrappers.StringValue{Value: hc.GetDescription()}, CreatedTime: hc.CreateTime.GetTimestamp(), UpdatedTime: hc.UpdateTime.GetTimestamp(), Type: "static", @@ -132,7 +123,98 @@ func TestGet(t *testing.T) { req := proto.Clone(toMerge).(*pbs.GetHostCatalogRequest) proto.Merge(req, tc.req) - s, err := host_catalogs.NewService(repo, iamRepoFn) + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) + require.NoError(err, "Couldn't create a new host catalog service.") + + got, gErr := s.GetHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "GetHostCatalog(%+v) got error %v, wanted %v", req, gErr, tc.err) + } + + if tc.res != nil { + tc.res.Item.Version = 1 + } + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetHostCatalog(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + +func TestGet_Plugin(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } + name := "test" + plg := host.TestPlugin(t, conn, name, name) + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + + toMerge := &pbs.GetHostCatalogRequest{ + Id: hc.GetPublicId(), + } + + pHostCatalog := &pb.HostCatalog{ + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, + CreatedTime: hc.CreateTime.GetTimestamp(), + UpdatedTime: hc.UpdateTime.GetTimestamp(), + Type: name, + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + } + + cases := []struct { + name string + req *pbs.GetHostCatalogRequest + res *pbs.GetHostCatalogResponse + err error + }{ + { + name: "Get an Existing HostCatalog", + req: &pbs.GetHostCatalogRequest{Id: hc.GetPublicId()}, + res: &pbs.GetHostCatalogResponse{Item: pHostCatalog}, + }, + { + name: "Get a non existing Host Catalog", + req: &pbs.GetHostCatalogRequest{Id: plugin.HostCatalogPrefix + "_DoesntExis"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Wrong id prefix", + req: &pbs.GetHostCatalogRequest{Id: "j_1234567890"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "space in id", + req: &pbs.GetHostCatalogRequest{Id: plugin.HostCatalogPrefix + "_1 23456789"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + req := proto.Clone(toMerge).(*pbs.GetHostCatalogRequest) + proto.Merge(req, tc.req) + + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(err, "Couldn't create a new host catalog service.") got, gErr := s.GetHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) @@ -157,6 +239,12 @@ func TestList(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iam.TestRepo(t, conn, wrapper), nil } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -181,6 +269,26 @@ func TestList(t *testing.T) { }) } + var testPluginCatalogs []*pb.HostCatalog + name := "test" + plg := host.TestPlugin(t, conn, name, name) + for i := 0 ; i < 3; i++ { + hc := plugin.TestCatalog(t, conn, pWithCatalogs.GetPublicId(), plg.GetPublicId()) + cat := &pb.HostCatalog{ + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + CreatedTime: hc.GetCreateTime().GetTimestamp(), + UpdatedTime: hc.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithCatalogs.GetPublicId()}, + Version: 1, + Type: name, + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + } + wantSomeCatalogs = append(wantSomeCatalogs, cat) + testPluginCatalogs = append(testPluginCatalogs, cat) + } + var wantOtherCatalogs []*pb.HostCatalog for _, hc := range static.TestCatalogs(t, conn, pWithOtherCatalogs.GetPublicId(), 3) { wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{ @@ -196,6 +304,23 @@ func TestList(t *testing.T) { }) } + name = "different" + plg = host.TestPlugin(t, conn, name, name) + for i := 0 ; i < 3; i++ { + hc := plugin.TestCatalog(t, conn, pWithOtherCatalogs.GetPublicId(), plg.GetPublicId()) + wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{ + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + CreatedTime: hc.GetCreateTime().GetTimestamp(), + UpdatedTime: hc.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithOtherCatalogs.GetPublicId()}, + Version: 1, + Type: name, + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + }) + } + cases := []struct { name string req *pbs.ListHostCatalogsRequest @@ -242,6 +367,14 @@ func TestList(t *testing.T) { }, res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs}, }, + { + name: "Filter To Catalog Test Type", + req: &pbs.ListHostCatalogsRequest{ + ScopeId: scope.Global.String(), Recursive: true, + Filter: `"/item/type"=="test"`, + }, + res: &pbs.ListHostCatalogsResponse{Items: testPluginCatalogs}, + }, { name: "Filter To No Catalogs", req: &pbs.ListHostCatalogsRequest{ScopeId: pWithCatalogs.GetPublicId(), Filter: `"/item/id"=="doesnt match"`}, @@ -256,7 +389,7 @@ func TestList(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := host_catalogs.NewService(repoFn, iamRepoFn) + s, err := host_catalogs.NewService(repoFn, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(err, "Couldn't create new auth_method service.") // Test with non-anon user @@ -266,6 +399,13 @@ func TestList(t *testing.T) { assert.True(errors.Is(gErr, tc.err), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err) return } + sort.Slice(got.Items, func(i, j int) bool { + return got.Items[i].GetId() < got.Items[j].GetId() + }) + sort.Slice(tc.res.Items, func(i, j int) bool { + return tc.res.Items[i].GetId() < tc.res.Items[j].GetId() + }) + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res) // Test with anon user @@ -283,9 +423,27 @@ func TestList(t *testing.T) { func TestDelete(t *testing.T) { t.Parallel() - hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] - s, err := host_catalogs.NewService(repo, iamRepoFn) + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(t, err, "Couldn't create a new host catalog service.") cases := []struct { @@ -335,9 +493,27 @@ func TestDelete(t *testing.T) { func TestDelete_twice(t *testing.T) { t.Parallel() assert, require := assert.New(t), require.New(t) - hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] - s, err := host_catalogs.NewService(repo, iamRepoFn) + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(err, "Couldn't create a new host catalog service.") req := &pbs.DeleteHostCatalogRequest{ Id: hc.GetPublicId(), @@ -350,9 +526,27 @@ func TestDelete_twice(t *testing.T) { assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.") } -func TestCreate(t *testing.T) { +func TestCreate_Static(t *testing.T) { t.Parallel() - defaultHc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + defaultHc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] defaultHcCreated := defaultHc.GetCreateTime().GetTimestamp().AsTime() toMerge := &pbs.CreateHostCatalogRequest{} @@ -451,7 +645,7 @@ func TestCreate(t *testing.T) { req := proto.Clone(toMerge).(*pbs.CreateHostCatalogRequest) proto.Merge(req, tc.req) - s, err := host_catalogs.NewService(repo, iamRepoFn) + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(err, "Failed to create a new host catalog service.") got, gErr := s.CreateHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) @@ -483,12 +677,192 @@ func TestCreate(t *testing.T) { } } +func TestCreate_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + name := "test" + plg := host.TestPlugin(t, conn, name, name) + defaultHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + defaultHcCreated := defaultHc.GetCreateTime().GetTimestamp().AsTime() + toMerge := &pbs.CreateHostCatalogRequest{} + + cases := []struct { + name string + req *pbs.CreateHostCatalogRequest + res *pbs.CreateHostCatalogResponse + err error + }{ + { + name: "Create a valid HostCatalog", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + ScopeId: proj.GetPublicId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + }}, + res: &pbs.CreateHostCatalogResponse{ + Uri: fmt.Sprintf("host-catalogs/%s_%s_", plugin.HostCatalogPrefix, name), + Item: &pb.HostCatalog{ + ScopeId: proj.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + }, + }, + }, + { + name: "Cant create in org", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + ScopeId: proj.GetParentId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant create in global", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + ScopeId: scope.Global.String(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Create with unknown type", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: "thisismadeup", + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Create with no type", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Id", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + Id: "not allowed to be set", + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Created Time", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + CreatedTime: timestamppb.Now(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Update Time", + req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + UpdatedTime: timestamppb.Now(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + req := proto.Clone(toMerge).(*pbs.CreateHostCatalogRequest) + proto.Merge(req, tc.req) + + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) + require.NoError(err, "Failed to create a new host catalog service.") + + got, gErr := s.CreateHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "CreateHostCatalog(%+v) got error %v, wanted %v", req, gErr, tc.err) + return + } + require.NoError(gErr) + if got != nil { + assert.Contains(got.GetUri(), tc.res.GetUri()) + assert.True(strings.HasPrefix(got.GetItem().GetId(), fmt.Sprintf("%s_%s_", plugin.HostCatalogPrefix, name))) + gotCreateTime := got.GetItem().GetCreatedTime().AsTime() + require.NoError(err, "Error converting proto to timestamp.") + gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime() + require.NoError(err, "Error converting proto to timestamp") + // Verify it is a catalog created after the test setup's default catalog + assert.True(gotCreateTime.After(defaultHcCreated), "New catalog should have been created after default catalog. Was created %v, which is after %v", gotCreateTime, defaultHcCreated) + assert.True(gotUpdateTime.After(defaultHcCreated), "New catalog should have been updated after default catalog. Was updated %v, which is after %v", gotUpdateTime, defaultHcCreated) + + // Clear all values which are hard to compare against. + got.Uri, tc.res.Uri = "", "" + got.Item.Id, tc.res.Item.Id = "", "" + got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil + } + if tc.res != nil { + tc.res.Item.Version = 1 + } + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CreateHostCatalog(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + func TestUpdate(t *testing.T) { t.Parallel() - hc, proj, repoFn, iamRepoFn := createDefaultHostCatalogAndRepo(t) - tested, err := host_catalogs.NewService(repoFn, iamRepoFn) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + tested, err := host_catalogs.NewService(repoFn, pluginHostRepo, pluginRepo, iamRepoFn) require.NoError(t, err, "Failed to create a new host catalog service.") + hc, err := static.NewHostCatalog(proj.GetPublicId(), static.WithName("default"), static.WithDescription("default")) + require.NoError(t, err, "Couldn't get new catalog.") + repo, err := repoFn() + require.NoError(t, err, "Couldn't create static repostitory") + hc, err = repo.CreateCatalog(context.Background(), hc) + require.NoError(t, err, "Couldn't persist new catalog.") + var version uint32 = 1 resetHostCatalog := func() { @@ -797,7 +1171,9 @@ func TestUpdate(t *testing.T) { if tc.err != nil { require.Error(gErr) assert.True(errors.Is(gErr, tc.err), "UpdateHostCatalog(%+v) got error %v, wanted %v", req, gErr, tc.err) + return } + require.NoError(gErr) if tc.err == nil { defer resetHostCatalog() From a8eb5055707e0a32fe28cc8915d6b5f540d4ca44 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 16 Sep 2021 14:57:47 -0400 Subject: [PATCH 16/71] Fix collision in migration version --- .../postgres/{16 => 17}/01_plugin.up.sql | 0 .../postgres/{16 => 17}/02_hostplugin.up.sql | 0 .../postgres/{16 => 17}/03_host.up.sql | 0 .../postgres/{16 => 17}/04_plugin_host.up.sql | 0 internal/db/schema/postgres_migration.gen.go | 702 +++++++++--------- 5 files changed, 351 insertions(+), 351 deletions(-) rename internal/db/schema/migrations/postgres/{16 => 17}/01_plugin.up.sql (100%) rename internal/db/schema/migrations/postgres/{16 => 17}/02_hostplugin.up.sql (100%) rename internal/db/schema/migrations/postgres/{16 => 17}/03_host.up.sql (100%) rename internal/db/schema/migrations/postgres/{16 => 17}/04_plugin_host.up.sql (100%) diff --git a/internal/db/schema/migrations/postgres/16/01_plugin.up.sql b/internal/db/schema/migrations/postgres/17/01_plugin.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/16/01_plugin.up.sql rename to internal/db/schema/migrations/postgres/17/01_plugin.up.sql diff --git a/internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql b/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/16/02_hostplugin.up.sql rename to internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql diff --git a/internal/db/schema/migrations/postgres/16/03_host.up.sql b/internal/db/schema/migrations/postgres/17/03_host.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/16/03_host.up.sql rename to internal/db/schema/migrations/postgres/17/03_host.up.sql diff --git a/internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql b/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/16/04_plugin_host.up.sql rename to internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 476f7a514..1929a5e72 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -4,7 +4,7 @@ package schema func init() { migrationStates["postgres"] = migrationState{ - binarySchemaVersion: 16005, + binarySchemaVersion: 17004, upMigrations: map[int][]byte{ 1: []byte(` create domain wt_public_id as text @@ -6940,82 +6940,6 @@ alter table wh_host_dimension $$ language plpgsql; `), 16001: []byte(` --- We are updating the plugin table here to have a scope id, since - -- subtypes of plugin should be scoped. We use 'global' as the default - -- just for the purpose of the migration since the scope cannot be null - -- and the one plugin that already exists (pi_system) can be globally - -- scoped. The immediately following statement removes this default. - -- We add a name so we can enforce that all plugins have a unique name - -- in a specific scope across all plugin subtypes. - alter table plugin - add column name wt_name, - add column scope_id wt_scope_id - not null - default 'global' - constraint iam_scope_global_fkey - references iam_scope_global(scope_id) - on delete cascade - on update cascade; - - alter table plugin - alter column scope_id drop default; - - -- Add constraints that enforce names are unique across all plugin subtypes - -- in a specific scope. - alter table plugin - add constraint plugin_scope_id_public_id_uq - unique (scope_id, public_id), - add constraint plugin_scope_id_name_uq - unique (scope_id, name); - - - -- insert, update, and delete plugin_subtypes are created since we are adding - -- subtyped plugins and we need to keep the base table plugin in sync with all - -- subtype tables. - create function insert_plugin_subtype() - returns trigger - as $$ - begin - insert into plugin - (public_id, scope_id, name) - values - (new.public_id, new.scope_id, new.name); - return new; - end; - $$ language plpgsql; - comment on function insert_plugin_subtype() is - 'insert_plugin_subtype() inserts sub type name into the base type plugin table'; - - create function update_plugin_subtype() - returns trigger - as $$ - begin - update plugin set name = new.name where public_id = new.public_id and new.name != name; - return new; - end; - $$ language plpgsql; - comment on function update_plugin_subtype() is - 'update_plugin_subtype() will update base plugin type name column with new values from sub type'; - - -- delete_plugin_subtype() is an after delete trigger function - -- for subtypes of plugin - create function delete_plugin_subtype() - returns trigger - as $$ - begin - delete from plugin - where public_id = old.public_id; - return null; -- result is ignored since this is an after trigger - end; - $$ language plpgsql; - comment on function delete_plugin_subtype is - 'delete_plugin_subtype() is an after trigger function for subytypes of plugin'; - - insert into oplog_ticket (name, version) - values - ('plugin', 1); -`), - 16001: []byte(` -- replaces check from internal/db/schema/migrations/postgres/0/60_wh_domain_types.up.sql alter domain wh_public_id drop constraint wh_public_id_check; alter domain wh_public_id add constraint wh_public_id_check @@ -7144,93 +7068,6 @@ alter table wh_host_dimension ('Unknown', 'Unknown'); `), 16002: []byte(` -/* - ┌──────────────────┐ - │ plugin │ - ├──────────────────┤ - │public_id (pk) │ - │scope_id (fk) │ - └──────────────────┘ - ┼ - ┼ - │ - ┼ - ┼ - ┌──────────────────┐ - │ plugin_host │ - ├──────────────────┤ - │public_id (pk) │ - │scope_id (fk) │ - │name │ - │description │ - │version │ - │plugin_name │ - │id_prefix │ - └──────────────────┘ -*/ - create table plugin_host ( - public_id wt_plugin_id primary key, - scope_id wt_scope_id not null - -- TODO: Allow plugins to be created in different scopes and - -- constrain the host-catalog's plugin reference accordingly. - constraint iam_scope_global_fkey - references iam_scope_global(scope_id) - on delete cascade - on update cascade, - name wt_name, - description text, - create_time wt_timestamp, - update_time wt_timestamp, - version wt_version, - plugin_name text not null - constraint plugin_name_must_be_not_empty - check(length(trim(plugin_name)) > 0) - constraint plugin_name_must_be_lowercase - check(lower(trim(plugin_name)) = plugin_name) - constraint plugin_host_plugin_name_uq - unique, - id_prefix text not null - constraint plugin_id_prefix_must_be_not_empty - check(length(trim(id_prefix)) > 0) - constraint plugin_id_prefix_must_fit_format - check (id_prefix ~ '^[a-z0-9]*$') - constraint plugin_host_id_prefix_uq - unique, - constraint plugin_fkey - foreign key (scope_id, public_id) - references plugin(scope_id, public_id) - on delete cascade - on update cascade, - constraint plugin_host_scope_id_name_uq - unique(scope_id, name) - ); - - create trigger update_version_column after update on plugin_host - for each row execute procedure update_version_column(); - - create trigger update_time_column before update on plugin_host - for each row execute procedure update_time_column(); - - create trigger default_create_time_column before insert on plugin_host - for each row execute procedure default_create_time(); - - create trigger immutable_columns before update on plugin_host - for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); - - create trigger insert_plugin_subtype before insert on plugin_host - for each row execute procedure insert_plugin_subtype(); - - create trigger update_plugin_subtype before update on plugin_host - for each row execute procedure update_plugin_subtype(); - - create trigger delete_plugin_subtype after delete on plugin_host - for each row execute procedure delete_plugin_subtype(); - - insert into oplog_ticket (name, version) - values - ('plugin_host', 1); -`), - 16002: []byte(` -- The whx_credential_dimension_source and whx_credential_dimension_target views are used -- by an insert trigger to determine if the current row for the dimension has -- changed and a new one needs to be inserted. The first column in the target view @@ -7331,65 +7168,6 @@ alter table wh_host_dimension ; `), 16003: []byte(` --- We are adding the name to the base host catalog type. This allows the db - -- to ensure that catalog names are unique in a scope across all subtypes. - alter table host_catalog - add column name wt_name; - - alter table host_catalog - add constraint host_catalog_scope_id_name_uq - unique (scope_id, name); - - -- Now that we've added the name column to the base type, we copy - -- the name from the static host catalog table into the base table. - update host_catalog - set name = st.name - from - static_host_catalog st - where - host_catalog.public_id = st.public_id and - st.name is not null; - - -- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql - -- to include the name. - -- insert_host_catalog_subtype() is a before insert trigger - -- function for subtypes of host_catalog - create or replace function insert_host_catalog_subtype() - returns trigger - as $$ - begin - insert into host_catalog - (public_id, scope_id, name) - values - (new.public_id, new.scope_id, new.name); - return new; - end; - $$ language plpgsql; - comment on function insert_host_catalog_subtype() is - 'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table'; - - -- Now that we are tracking the name, which is mutable, we need to also - -- update the base table when the subtype tables are updated. - -- update_host_catalog_subtype() is intended to be used as a before update - -- trigger for all host catalog sub types. The purpose is to ensure that the - -- base table for host catalog to contain the updated names for each host catalog - -- in order to enforce uniqueness across all host catalogs, regardless of subtype, - -- in a given scope. - create function update_host_catalog_subtype() - returns trigger - as $$ - begin - update host_catalog set name = new.name where public_id = new.public_id and new.name != name; - return new; - end; - $$ language plpgsql; - comment on function update_host_catalog_subtype() is - 'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type'; - - create trigger update_host_catalog_subtype before update on static_host_catalog - for each row execute procedure update_host_catalog_subtype(); -`), - 16003: []byte(` -- wh_upsert_credential_dimension compares the current vaules in the wh_credential_dimension -- with the current values in the operational tables for the given parameters. IF the values -- between operational tables and the wh_credential_dimension differ, a new row is inserted in @@ -7560,6 +7338,356 @@ alter table wh_host_dimension execute function wh_upsert_credentail_group(); `), 16004: []byte(` +alter table wh_session_accumulating_fact + add column credential_group_key wh_dim_key not null + default 'Unknown' + references wh_credential_group (key) + on delete restrict + on update cascade; + alter table wh_session_accumulating_fact + alter column credential_group_key drop default; + + -- replaces function from 15/01_wh_rename_key_columns.up.sql + drop trigger wh_insert_session on session; + drop function wh_insert_session; + create function wh_insert_session() + returns trigger + as $$ + declare + new_row wh_session_accumulating_fact%rowtype; + begin + with + pending_timestamp (date_dim_key, time_dim_key, ts) as ( + select wh_date_key(start_time), wh_time_key(start_time), start_time + from session_state + where session_id = new.public_id + and state = 'pending' + ) + insert into wh_session_accumulating_fact ( + session_id, + auth_token_id, + host_key, + user_key, + credential_group_key, + session_pending_date_key, + session_pending_time_key, + session_pending_time + ) + select new.public_id, + new.auth_token_id, + wh_upsert_host(new.host_id, new.host_set_id, new.target_id), + wh_upsert_user(new.user_id, new.auth_token_id), + 'no credentials', -- will be updated by wh_upsert_credentail_group + pending_timestamp.date_dim_key, + pending_timestamp.time_dim_key, + pending_timestamp.ts + from pending_timestamp + returning * into strict new_row; + return null; + end; + $$ language plpgsql; + create trigger wh_insert_session + after insert on session + for each row + execute function wh_insert_session(); +`), + 16005: []byte(` +alter table wh_session_connection_accumulating_fact + add column credential_group_key wh_dim_key not null + default 'Unknown' + references wh_credential_group (key) + on delete restrict + on update cascade; + alter table wh_session_connection_accumulating_fact + alter column credential_group_key drop default; + + -- replaces function from 15/01_wh_rename_key_columns.up.sql + drop trigger wh_insert_session_connection on session_connection; + drop function wh_insert_session_connection; + create function wh_insert_session_connection() + returns trigger + as $$ + declare + new_row wh_session_connection_accumulating_fact%rowtype; + begin + with + authorized_timestamp (date_dim_key, time_dim_key, ts) as ( + select wh_date_key(start_time), wh_time_key(start_time), start_time + from session_connection_state + where connection_id = new.public_id + and state = 'authorized' + ), + session_dimension (host_dim_key, user_dim_key, credential_group_dim_key) as ( + select host_key, user_key, credential_group_key + from wh_session_accumulating_fact + where session_id = new.session_id + ) + insert into wh_session_connection_accumulating_fact ( + connection_id, + session_id, + host_key, + user_key, + credential_group_key, + connection_authorized_date_key, + connection_authorized_time_key, + connection_authorized_time, + client_tcp_address, + client_tcp_port_number, + endpoint_tcp_address, + endpoint_tcp_port_number, + bytes_up, + bytes_down + ) + select new.public_id, + new.session_id, + session_dimension.host_dim_key, + session_dimension.user_dim_key, + session_dimension.credential_group_dim_key, + authorized_timestamp.date_dim_key, + authorized_timestamp.time_dim_key, + authorized_timestamp.ts, + new.client_tcp_address, + new.client_tcp_port, + new.endpoint_tcp_address, + new.endpoint_tcp_port, + new.bytes_up, + new.bytes_down + from authorized_timestamp, + session_dimension + returning * into strict new_row; + perform wh_rollup_connections(new.session_id); + return null; + end; + $$ language plpgsql; + + create trigger wh_insert_session_connection + after insert on session_connection + for each row + execute function wh_insert_session_connection(); +`), + 17001: []byte(` +-- We are updating the plugin table here to have a scope id, since + -- subtypes of plugin should be scoped. We use 'global' as the default + -- just for the purpose of the migration since the scope cannot be null + -- and the one plugin that already exists (pi_system) can be globally + -- scoped. The immediately following statement removes this default. + -- We add a name so we can enforce that all plugins have a unique name + -- in a specific scope across all plugin subtypes. + alter table plugin + add column name wt_name, + add column scope_id wt_scope_id + not null + default 'global' + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade; + + alter table plugin + alter column scope_id drop default; + + -- Add constraints that enforce names are unique across all plugin subtypes + -- in a specific scope. + alter table plugin + add constraint plugin_scope_id_public_id_uq + unique (scope_id, public_id), + add constraint plugin_scope_id_name_uq + unique (scope_id, name); + + + -- insert, update, and delete plugin_subtypes are created since we are adding + -- subtyped plugins and we need to keep the base table plugin in sync with all + -- subtype tables. + create function insert_plugin_subtype() + returns trigger + as $$ + begin + insert into plugin + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_plugin_subtype() is + 'insert_plugin_subtype() inserts sub type name into the base type plugin table'; + + create function update_plugin_subtype() + returns trigger + as $$ + begin + update plugin set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_plugin_subtype() is + 'update_plugin_subtype() will update base plugin type name column with new values from sub type'; + + -- delete_plugin_subtype() is an after delete trigger function + -- for subtypes of plugin + create function delete_plugin_subtype() + returns trigger + as $$ + begin + delete from plugin + where public_id = old.public_id; + return null; -- result is ignored since this is an after trigger + end; + $$ language plpgsql; + comment on function delete_plugin_subtype is + 'delete_plugin_subtype() is an after trigger function for subytypes of plugin'; + + insert into oplog_ticket (name, version) + values + ('plugin', 1); +`), + 17002: []byte(` +/* + ┌──────────────────┐ + │ plugin │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + └──────────────────┘ + ┼ + ┼ + │ + ┼ + ┼ + ┌──────────────────┐ + │ plugin_host │ + ├──────────────────┤ + │public_id (pk) │ + │scope_id (fk) │ + │name │ + │description │ + │version │ + │plugin_name │ + │id_prefix │ + └──────────────────┘ +*/ + create table plugin_host ( + public_id wt_plugin_id primary key, + scope_id wt_scope_id not null + -- TODO: Allow plugins to be created in different scopes and + -- constrain the host-catalog's plugin reference accordingly. + constraint iam_scope_global_fkey + references iam_scope_global(scope_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + plugin_name text not null + constraint plugin_name_must_be_not_empty + check(length(trim(plugin_name)) > 0) + constraint plugin_name_must_be_lowercase + check(lower(trim(plugin_name)) = plugin_name) + constraint plugin_host_plugin_name_uq + unique, + id_prefix text not null + constraint plugin_id_prefix_must_be_not_empty + check(length(trim(id_prefix)) > 0) + constraint plugin_id_prefix_must_fit_format + check (id_prefix ~ '^[a-z0-9]*$') + constraint plugin_host_id_prefix_uq + unique, + constraint plugin_fkey + foreign key (scope_id, public_id) + references plugin(scope_id, public_id) + on delete cascade + on update cascade, + constraint plugin_host_scope_id_name_uq + unique(scope_id, name) + ); + + create trigger update_version_column after update on plugin_host + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on plugin_host + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on plugin_host + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on plugin_host + for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); + + create trigger insert_plugin_subtype before insert on plugin_host + for each row execute procedure insert_plugin_subtype(); + + create trigger update_plugin_subtype before update on plugin_host + for each row execute procedure update_plugin_subtype(); + + create trigger delete_plugin_subtype after delete on plugin_host + for each row execute procedure delete_plugin_subtype(); + + insert into oplog_ticket (name, version) + values + ('plugin_host', 1); +`), + 17003: []byte(` +-- We are adding the name to the base host catalog type. This allows the db + -- to ensure that catalog names are unique in a scope across all subtypes. + alter table host_catalog + add column name wt_name; + + alter table host_catalog + add constraint host_catalog_scope_id_name_uq + unique (scope_id, name); + + -- Now that we've added the name column to the base type, we copy + -- the name from the static host catalog table into the base table. + update host_catalog + set name = st.name + from + static_host_catalog st + where + host_catalog.public_id = st.public_id and + st.name is not null; + + -- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql + -- to include the name. + -- insert_host_catalog_subtype() is a before insert trigger + -- function for subtypes of host_catalog + create or replace function insert_host_catalog_subtype() + returns trigger + as $$ + begin + insert into host_catalog + (public_id, scope_id, name) + values + (new.public_id, new.scope_id, new.name); + return new; + end; + $$ language plpgsql; + comment on function insert_host_catalog_subtype() is + 'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table'; + + -- Now that we are tracking the name, which is mutable, we need to also + -- update the base table when the subtype tables are updated. + -- update_host_catalog_subtype() is intended to be used as a before update + -- trigger for all host catalog sub types. The purpose is to ensure that the + -- base table for host catalog to contain the updated names for each host catalog + -- in order to enforce uniqueness across all host catalogs, regardless of subtype, + -- in a given scope. + create function update_host_catalog_subtype() + returns trigger + as $$ + begin + update host_catalog set name = new.name where public_id = new.public_id and new.name != name; + return new; + end; + $$ language plpgsql; + comment on function update_host_catalog_subtype() is + 'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type'; + + create trigger update_host_catalog_subtype before update on static_host_catalog + for each row execute procedure update_host_catalog_subtype(); +`), + 17004: []byte(` /* ┌──────────────────┐ │ plugin_host │ @@ -7723,134 +7851,6 @@ alter table wh_host_dimension ('host_plugin_catalog', 1), ('host_plugin_catalog_secret', 1), ('host_plugin_set', 1); -`), - 16004: []byte(` -alter table wh_session_accumulating_fact - add column credential_group_key wh_dim_key not null - default 'Unknown' - references wh_credential_group (key) - on delete restrict - on update cascade; - alter table wh_session_accumulating_fact - alter column credential_group_key drop default; - - -- replaces function from 15/01_wh_rename_key_columns.up.sql - drop trigger wh_insert_session on session; - drop function wh_insert_session; - create function wh_insert_session() - returns trigger - as $$ - declare - new_row wh_session_accumulating_fact%rowtype; - begin - with - pending_timestamp (date_dim_key, time_dim_key, ts) as ( - select wh_date_key(start_time), wh_time_key(start_time), start_time - from session_state - where session_id = new.public_id - and state = 'pending' - ) - insert into wh_session_accumulating_fact ( - session_id, - auth_token_id, - host_key, - user_key, - credential_group_key, - session_pending_date_key, - session_pending_time_key, - session_pending_time - ) - select new.public_id, - new.auth_token_id, - wh_upsert_host(new.host_id, new.host_set_id, new.target_id), - wh_upsert_user(new.user_id, new.auth_token_id), - 'no credentials', -- will be updated by wh_upsert_credentail_group - pending_timestamp.date_dim_key, - pending_timestamp.time_dim_key, - pending_timestamp.ts - from pending_timestamp - returning * into strict new_row; - return null; - end; - $$ language plpgsql; - create trigger wh_insert_session - after insert on session - for each row - execute function wh_insert_session(); -`), - 16005: []byte(` -alter table wh_session_connection_accumulating_fact - add column credential_group_key wh_dim_key not null - default 'Unknown' - references wh_credential_group (key) - on delete restrict - on update cascade; - alter table wh_session_connection_accumulating_fact - alter column credential_group_key drop default; - - -- replaces function from 15/01_wh_rename_key_columns.up.sql - drop trigger wh_insert_session_connection on session_connection; - drop function wh_insert_session_connection; - create function wh_insert_session_connection() - returns trigger - as $$ - declare - new_row wh_session_connection_accumulating_fact%rowtype; - begin - with - authorized_timestamp (date_dim_key, time_dim_key, ts) as ( - select wh_date_key(start_time), wh_time_key(start_time), start_time - from session_connection_state - where connection_id = new.public_id - and state = 'authorized' - ), - session_dimension (host_dim_key, user_dim_key, credential_group_dim_key) as ( - select host_key, user_key, credential_group_key - from wh_session_accumulating_fact - where session_id = new.session_id - ) - insert into wh_session_connection_accumulating_fact ( - connection_id, - session_id, - host_key, - user_key, - credential_group_key, - connection_authorized_date_key, - connection_authorized_time_key, - connection_authorized_time, - client_tcp_address, - client_tcp_port_number, - endpoint_tcp_address, - endpoint_tcp_port_number, - bytes_up, - bytes_down - ) - select new.public_id, - new.session_id, - session_dimension.host_dim_key, - session_dimension.user_dim_key, - session_dimension.credential_group_dim_key, - authorized_timestamp.date_dim_key, - authorized_timestamp.time_dim_key, - authorized_timestamp.ts, - new.client_tcp_address, - new.client_tcp_port, - new.endpoint_tcp_address, - new.endpoint_tcp_port, - new.bytes_up, - new.bytes_down - from authorized_timestamp, - session_dimension - returning * into strict new_row; - perform wh_rollup_connections(new.session_id); - return null; - end; - $$ language plpgsql; - - create trigger wh_insert_session_connection - after insert on session_connection - for each row - execute function wh_insert_session_connection(); `), 2001: []byte(` -- log_migration entries represent logs generated during migrations From 01d4383c1bf2f44b481a3b14fb6461b49030240c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 20 Sep 2021 12:48:38 -0400 Subject: [PATCH 17/71] Add preferred endpoints logic to API/handler/DB and some CLI bits (#1529) --- api/hostsets/host_set.gen.go | 25 +- api/hostsets/option.gen.go | 12 + globals/fields.go | 1 + internal/cmd/commands.go | 12 + internal/cmd/commands/hostsetscmd/funcs.go | 3 + .../cmd/commands/hostsetscmd/plugin_funcs.go | 97 +++++++ .../hostsetscmd/plugin_hostsets.gen.go | 241 ++++++++++++++++++ internal/cmd/gencli/input.go | 14 + .../postgres/17/05_preferred_endpoints.up.sql | 73 ++++++ .../postgres/17/preferred_endpoints_test.go | 166 ++++++++++++ internal/db/schema/postgres_migration.gen.go | 73 +++++- internal/gen/controller.swagger.json | 7 + internal/host/options.go | 46 ++++ internal/host/options_test.go | 34 +++ internal/host/plugin/host_catalog_secret.go | 2 +- .../host/plugin/host_catalog_secret_test.go | 2 + internal/host/plugin/host_catalog_test.go | 2 +- internal/host/plugin/host_set.go | 14 +- internal/host/plugin/host_set_test.go | 16 ++ internal/host/plugin/immutable_fields_test.go | 6 +- internal/host/plugin/options.go | 18 +- internal/host/plugin/options_test.go | 8 +- internal/host/plugin/repository.go | 14 +- .../host/plugin/repository_host_catalog.go | 19 +- .../plugin/repository_host_catalog_test.go | 2 +- internal/host/plugin/repository_host_set.go | 217 ++++++++++++++-- .../host/plugin/repository_host_set_test.go | 82 +++--- internal/host/plugin/repository_test.go | 5 +- internal/host/plugin/store/host.pb.go | 65 +++-- internal/host/plugin/testing.go | 31 +-- internal/host/plugin/testing_test.go | 4 +- internal/host/preferred_endpoint.go | 87 +++++++ internal/host/preferred_endpoint_test.go | 219 ++++++++++++++++ internal/host/store/host.pb.go | 109 +++++++- internal/libs/endpoint/preferencer.go | 2 +- .../api/resources/hostsets/v1/host_set.proto | 4 + .../storage/host/plugin/store/v1/host.proto | 8 +- .../storage/host/store/v1/host.proto | 14 + internal/servers/controller/controller.go | 6 +- .../host_catalogs/host_catalog_service.go | 2 +- .../host_catalog_service_test.go | 4 +- .../handlers/host_sets/host_set_service.go | 50 +++- .../host_sets/host_set_service_test.go | 88 +++++-- .../handlers/targets/target_service.go | 10 +- .../handlers/targets/target_service_test.go | 12 +- .../api/resources/hostsets/host_set.pb.go | 42 ++- 46 files changed, 1737 insertions(+), 231 deletions(-) create mode 100644 internal/cmd/commands/hostsetscmd/plugin_funcs.go create mode 100644 internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go create mode 100644 internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql create mode 100644 internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go create mode 100644 internal/host/options.go create mode 100644 internal/host/options_test.go create mode 100644 internal/host/preferred_endpoint.go create mode 100644 internal/host/preferred_endpoint_test.go diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index d316e87cb..58fceb29c 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -13,18 +13,19 @@ import ( ) type HostSet struct { - Id string `json:"id,omitempty"` - HostCatalogId string `json:"host_catalog_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Type string `json:"type,omitempty"` - HostIds []string `json:"host_ids,omitempty"` - Attributes map[string]interface{} `json:"attributes,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + HostCatalogId string `json:"host_catalog_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + HostIds []string `json:"host_ids,omitempty"` + PreferredEndpoints []string `json:"preferred_endpoints,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } diff --git a/api/hostsets/option.gen.go b/api/hostsets/option.gen.go index f27578eb9..317b79b83 100644 --- a/api/hostsets/option.gen.go +++ b/api/hostsets/option.gen.go @@ -97,3 +97,15 @@ func DefaultName() Option { o.postMap["name"] = nil } } + +func WithPreferredEndpoints(inPreferredEndpoints []string) Option { + return func(o *options) { + o.postMap["preferred_endpoints"] = inPreferredEndpoints + } +} + +func DefaultPreferredEndpoints() Option { + return func(o *options) { + o.postMap["preferred_endpoints"] = nil + } +} diff --git a/globals/fields.go b/globals/fields.go index 692c8ddb9..00f8b898d 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -58,4 +58,5 @@ const ( ApplicationCredentialLibrariesField = "application_credential_libraries" ApplicationCredentialSourceIdsField = "application_credential_source_ids" ApplicationCredentialSourcesField = "application_credential_sources" + PreferredEndpointsField = "preferred_endpoints" ) diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 1776da276..270e41562 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -560,6 +560,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "create", }, nil }, + "host-sets create plugin": func() (cli.Command, error) { + return &hostsetscmd.PluginCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, "host-sets update": func() (cli.Command, error) { return &hostsetscmd.Command{ Command: base.NewCommand(ui), @@ -572,6 +578,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "update", }, nil }, + "host-sets update plugin": func() (cli.Command, error) { + return &hostsetscmd.PluginCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, "host-sets add-hosts": func() (cli.Command, error) { return &hostsetscmd.Command{ Command: base.NewCommand(ui), diff --git a/internal/cmd/commands/hostsetscmd/funcs.go b/internal/cmd/commands/hostsetscmd/funcs.go index b3064c8af..933396332 100644 --- a/internal/cmd/commands/hostsetscmd/funcs.go +++ b/internal/cmd/commands/hostsetscmd/funcs.go @@ -253,6 +253,9 @@ func printItemTable(result api.GenericResult) string { if item.HostCatalogId != "" { nonAttributeMap["Host Catalog ID"] = item.HostCatalogId } + if item.PreferredEndpoints != nil { + nonAttributeMap["Preferred Endpoints"] = item.PreferredEndpoints + } maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap) diff --git a/internal/cmd/commands/hostsetscmd/plugin_funcs.go b/internal/cmd/commands/hostsetscmd/plugin_funcs.go new file mode 100644 index 000000000..fca90f444 --- /dev/null +++ b/internal/cmd/commands/hostsetscmd/plugin_funcs.go @@ -0,0 +1,97 @@ +package hostsetscmd + +import ( + "fmt" + + "github.com/hashicorp/boundary/api/hostsets" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/libs/endpoint" +) + +func init() { + extraPluginActionsFlagsMapFunc = extraPluginActionsFlagsMapFuncImpl + extraPluginFlagsFunc = extraPluginFlagsFuncImpl + extraPluginFlagsHandlingFunc = extraPluginFlagsHandlingFuncImpl +} + +type extraPluginCmdVars struct { + flagPreferredEndpoints []string +} + +func extraPluginActionsFlagsMapFuncImpl() map[string][]string { + return map[string][]string{ + "create": {"preferred-endpoint"}, + "update": {"preferred-endpoint"}, + } +} + +func (c *PluginCommand) extraPluginHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary host-sets create plugin [options] [args]", + "", + " Create a host set of a type provided by a plugin. Example:", + "", + ` $ boundary host-sets create plugin -plugin-id plg_1234567890 -name prodops -description "Plugin host-set for ProdOps"`, + "", + "", + }) + + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary host-sets update plugin [options] [args]", + "", + " Update a host set of a type provided by a plugin given its ID. Example:", + "", + ` $ boundary host-sets update plugin -id hsplg_1234567890 -name "devops" -description "Plugin host-set for DevOps"`, + "", + "", + }) + default: + helpStr = helpMap[c.Func]() + } + return helpStr + c.Flags().Help() +} + +func extraPluginFlagsFuncImpl(c *PluginCommand, set *base.FlagSets, f *base.FlagSet) { + fs := set.NewFlagSet("Plugin Host-Set Options") + + for _, name := range flagsPluginMap[c.Func] { + switch name { + case "preferred-endpoint": + fs.StringSliceVar(&base.StringSliceVar{ + Name: "preferred-endpoint", + Target: &c.flagPreferredEndpoints, + Usage: `An endpoint preference, specified by "cidr:" ` + + `or "dns:", specifying which IP address or DNS name out ` + + `of a host's available possibilities should be preferred. May be specified ` + + `multiple times, which will build up an in-order set of preferences. ` + + `If no preferences are specified, a value will be chosen _at random_ from ` + + `all available values. May not be valid for all plugin types.`, + }) + } + } +} + +func extraPluginFlagsHandlingFuncImpl(c *PluginCommand, _ *base.FlagSets, opts *[]hostsets.Option) bool { + switch len(c.flagPreferredEndpoints) { + case 0: + case 1: + if c.flagPreferredEndpoints[0] == "null" { + *opts = append(*opts, hostsets.DefaultPreferredEndpoints()) + break + } + fallthrough + + default: + if _, err := endpoint.NewPreferencer(c.Context, endpoint.WithPreferenceOrder(c.flagPreferredEndpoints)); err != nil { + c.UI.Error(fmt.Sprintf("Unable to successfully validate preferred endpoints: %s", err)) + return false + } + *opts = append(*opts, hostsets.WithPreferredEndpoints(c.flagPreferredEndpoints)) + } + + return true +} diff --git a/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go b/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go new file mode 100644 index 000000000..f47e6cc76 --- /dev/null +++ b/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go @@ -0,0 +1,241 @@ +// Code generated by "make cli"; DO NOT EDIT. +package hostsetscmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/hostsets" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initPluginFlags() { + flagsOnce.Do(func() { + extraFlags := extraPluginActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsPluginMap[k] = append(flagsPluginMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*PluginCommand)(nil) + _ cli.CommandAutocomplete = (*PluginCommand)(nil) +) + +type PluginCommand struct { + *base.Command + + Func string + + plural string + + extraPluginCmdVars +} + +func (c *PluginCommand) AutocompleteArgs() complete.Predictor { + initPluginFlags() + return complete.PredictAnything +} + +func (c *PluginCommand) AutocompleteFlags() complete.Flags { + initPluginFlags() + return c.Flags().Completions() +} + +func (c *PluginCommand) Synopsis() string { + if extra := extraPluginSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "host set" + + synopsisStr = fmt.Sprintf("%s %s", "plugin-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *PluginCommand) Help() string { + initPluginFlags() + + var helpStr string + helpMap := common.HelpMap("host set") + + switch c.Func { + default: + + helpStr = c.extraPluginHelpFunc(helpMap) + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsPluginMap = map[string][]string{ + + "create": {"host-catalog-id", "name", "description"}, + + "update": {"id", "name", "description", "version"}, +} + +func (c *PluginCommand) Flags() *base.FlagSets { + if len(flagsPluginMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "plugin-type host set", flagsPluginMap, c.Func) + + extraPluginFlagsFunc(c, set, f) + + return set +} + +func (c *PluginCommand) Run(args []string) int { + initPluginFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + } + + c.plural = "plugin-type host set" + switch c.Func { + case "list": + c.plural = "plugin-type host sets" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsPluginMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []hostsets.Option + + if strutil.StrListContains(flagsPluginMap[c.Func], "host-catalog-id") { + switch c.Func { + case "create": + if c.FlagHostCatalogId == "" { + c.PrintCliError(errors.New("HostCatalog ID must be passed in via -host-catalog-id or BOUNDARY_HOST_CATALOG_ID")) + return base.CommandUserError + } + } + } + + client, err := c.Client() + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %s", err.Error())) + return base.CommandCliError + } + hostsetsClient := hostsets.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, hostsets.DefaultName()) + default: + opts = append(opts, hostsets.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, hostsets.DefaultDescription()) + default: + opts = append(opts, hostsets.WithDescription(c.FlagDescription)) + } + + if c.FlagFilter != "" { + opts = append(opts, hostsets.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, hostsets.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + if ok := extraPluginFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + case "create": + result, err = hostsetsClient.Create(c.Context, c.FlagHostCatalogId, opts...) + + case "update": + result, err = hostsetsClient.Update(c.Context, c.FlagId, version, opts...) + + } + + result, err = executeExtraPluginActions(c, result, err, hostsetsClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomPluginActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraPluginActionsFlagsMapFunc = func() map[string][]string { return nil } + extraPluginSynopsisFunc = func(*PluginCommand) string { return "" } + extraPluginFlagsFunc = func(*PluginCommand, *base.FlagSets, *base.FlagSet) {} + extraPluginFlagsHandlingFunc = func(*PluginCommand, *base.FlagSets, *[]hostsets.Option) bool { return true } + executeExtraPluginActions = func(_ *PluginCommand, inResult api.GenericResult, inErr error, _ *hostsets.Client, _ uint32, _ []hostsets.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomPluginActionOutput = func(*PluginCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index a2640456e..8a044fecc 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -282,6 +282,20 @@ var inputStructs = map[string][]*cmdInfo{ HasDescription: true, VersionedActions: []string{"update"}, }, + { + ResourceType: resource.HostSet.String(), + Pkg: "hostsets", + StdActions: []string{"create", "update"}, + SubActionPrefix: "plugin", + HasExtraCommandVars: true, + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + Container: "HostCatalog", + HasDescription: true, + VersionedActions: []string{"update"}, + }, }, "hosts": { { diff --git a/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql b/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql new file mode 100644 index 000000000..b1478e9d5 --- /dev/null +++ b/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql @@ -0,0 +1,73 @@ +begin; + +create table host_set_preferred_endpoint ( + create_time wt_timestamp, + host_set_id wt_public_id not null + constraint host_set_fkey + references host_set(public_id) + on delete cascade + on update cascade, + priority int not null + constraint priority_must_be_greater_than_zero + check(priority > 0), + condition text not null + constraint condition_must_not_be_too_short + check(length(trim(condition)) > 4) -- minimum is 'dns:*' + constraint condition_must_not_be_too_long + check(length(trim(condition)) < 255) + constraint condition_has_valid_prefix + check( + left(trim(condition), 4) = 'dns:' + or + left(trim(condition), 5) = 'cidr:' + ) + constraint condition_does_not_contain_invalid_chars + check( + position('|' in trim(condition)) = 0 + and + position('=' in trim(condition)) = 0 + ), + primary key(host_set_id, priority), + constraint host_set_preferred_endpoint_host_set_id_condition_uq + unique(host_set_id, condition) +); + +-- host_set_immutable_preferred_endpoint() ensures that endpoint conditions +-- assigned to host sets are immutable. +create function + host_set_immutable_preferred_endpoint() + returns trigger +as $$ +begin + raise exception 'preferred endpoints are immutable'; +end; +$$ language plpgsql; + +create trigger immutable_preferred_endpoint + before update on host_set_preferred_endpoint + for each row execute procedure host_set_immutable_preferred_endpoint(); + +-- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its +-- associated value objects (preferred endpoints) as columns with delimited +-- values. The delimiter depends on the value objects (e.g. if they need +-- ordering). +create view host_plugin_host_set_with_value_obj as +select + hs.public_id, + hs.catalog_id, + hs.name, + hs.description, + hs.create_time, + hs.update_time, + hs.version, + hs.attributes, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints +from + host_plugin_set hs + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id +group by hs.public_id; +comment on view host_plugin_host_set_with_value_obj is +'host plugin host set with its associated value objects'; + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go b/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go new file mode 100644 index 000000000..fed33e577 --- /dev/null +++ b/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go @@ -0,0 +1,166 @@ +package migration + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/static" + "github.com/hashicorp/boundary/internal/iam" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_PreferredEndpointTable(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + rootWrapper := db.TestWrapper(t) + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, rootWrapper)) + hc := static.TestCatalogs(t, conn, proj.PublicId, 1)[0] + hs := static.TestSets(t, conn, hc.PublicId, 1)[0] + + type testCondition struct { + condition string + priority uint32 + } + insertTests := []struct { + testName string + hostSetId string + conditions []testCondition + wantErrContains string + }{ + { + testName: "invalid host set id", + hostSetId: "hsst_1234567890", + conditions: []testCondition{ + { + priority: 1, + condition: "dns:abcd", + }, + { + priority: 2, + condition: "cidr:1.2.3.4", + }, + }, + wantErrContains: "host_set_fkey", + }, + { + testName: "invalid condition prefix", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dsn:abcd", + }, + }, + wantErrContains: "condition_has_valid_prefix", + }, + { + testName: "invalid condition length", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dns:", + }, + }, + wantErrContains: "condition_must_not_be_too_short", + }, + { + testName: "invalid priority", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 0, + condition: "dns:abcd", + }, + }, + wantErrContains: "priority must not be empty", + }, + { + testName: "duplicate priority", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dns:abcd", + }, + { + priority: 1, + condition: "cidr:1.2.3.4", + }, + }, + wantErrContains: "host_set_preferred_endpoint_pkey", + }, + { + testName: "invalid char 1", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dns:ab|cd", + }, + { + priority: 1, + condition: "cidr:1.2.3.4", + }, + }, + wantErrContains: "condition_does_not_contain_invalid_chars", + }, + { + testName: "invalid char 2", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dns:abcd", + }, + { + priority: 1, + condition: "cidr:1.2=.3.4", + }, + }, + wantErrContains: "condition_does_not_contain_invalid_chars", + }, + { + testName: "valid", + hostSetId: hs.PublicId, + conditions: []testCondition{ + { + priority: 1, + condition: "dns:abcd", + }, + { + priority: 2, + condition: "cidr:1.2.3.4", + }, + }, + }, + } + for _, tt := range insertTests { + t.Run("insert: "+tt.testName, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + _, err := rw.Exec(ctx, "delete from host_set_preferred_endpoint where host_set_id = ?", []interface{}{tt.hostSetId}) + require.NoError(err) + + // Add items to insert + var items []interface{} + for _, cond := range tt.conditions { + ep := host.AllocPreferredEndpoint() + ep.HostSetId = tt.hostSetId + ep.Condition = cond.condition + ep.Priority = cond.priority + items = append(items, ep) + } + err = rw.CreateItems(ctx, items) + if tt.wantErrContains != "" { + require.Error(err) + assert.Contains(err.Error(), tt.wantErrContains) + return + } + require.NoError(err) + }) + } +} diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 1929a5e72..81c48f577 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -4,7 +4,7 @@ package schema func init() { migrationStates["postgres"] = migrationState{ - binarySchemaVersion: 17004, + binarySchemaVersion: 17005, upMigrations: map[int][]byte{ 1: []byte(` create domain wt_public_id as text @@ -7851,6 +7851,77 @@ alter table wh_session_connection_accumulating_fact ('host_plugin_catalog', 1), ('host_plugin_catalog_secret', 1), ('host_plugin_set', 1); +`), + 17005: []byte(` +create table host_set_preferred_endpoint ( + create_time wt_timestamp, + host_set_id wt_public_id not null + constraint host_set_fkey + references host_set(public_id) + on delete cascade + on update cascade, + priority int not null + constraint priority_must_be_greater_than_zero + check(priority > 0), + condition text not null + constraint condition_must_not_be_empty + check(length(trim(condition)) > 4) -- minimum is 'dns:*' + constraint condition_must_not_be_too_long + check(length(trim(condition)) < 255) + constraint condition_has_valid_prefix + check( + left(trim(condition), 4) = 'dns:' + or + left(trim(condition), 5) = 'cidr:' + ) + constraint condition_does_not_contain_invalid_chars + check( + position('|' in trim(condition)) = 0 + and + position('=' in trim(condition)) = 0 + ), + primary key(host_set_id, priority), + constraint host_set_preferred_endpoint_host_set_id_condition_uq + unique(host_set_id, condition) +); + +-- host_set_immutable_preferred_endpoint() ensures that endpoint conditions +-- assigned to host sets are immutable. +create function + host_set_immutable_preferred_endpoint() + returns trigger +as $$ +begin + raise exception 'preferred endpoints are immutable'; +end; +$$ language plpgsql; + +create trigger immutable_preferred_endpoint + before update on host_set_preferred_endpoint + for each row execute procedure host_set_immutable_preferred_endpoint(); + +-- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its +-- associated value objects (preferred endpoints) as columns with delimited +-- values. The delimiter depends on the value objects (e.g. if they need +-- ordering). +create view host_plugin_host_set_with_value_obj as +select + hs.public_id, + hs.catalog_id, + hs.name, + hs.description, + hs.create_time, + hs.update_time, + hs.version, + hs.attributes, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints +from + host_plugin_set hs + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id +group by hs.public_id; +comment on view host_plugin_host_set_with_value_obj is +'host plugin host set with its associated value objects'; `), 2001: []byte(` -- log_migration entries represent logs generated during migrations diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index ba95cd61e..708807b19 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -3863,6 +3863,13 @@ "description": "Output only. A list of Hosts in this Host Set.", "readOnly": true }, + "preferred_endpoints": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host." + }, "attributes": { "type": "object", "description": "The attributes that are applicable for the specific Host Set type." diff --git a/internal/host/options.go b/internal/host/options.go new file mode 100644 index 000000000..6e2afe4d6 --- /dev/null +++ b/internal/host/options.go @@ -0,0 +1,46 @@ +package host + +// GetOpts - iterate the inbound Options and return a struct +func GetOpts(opt ...Option) (options, error) { + opts := getDefaultOptions() + for _, o := range opt { + if err := o(&opts); err != nil { + return opts, err + } + } + return opts, nil +} + +// Option - how Options are passed as arguments. +type Option func(*options) error + +// options = how options are represented +type options struct { + WithLimit int + WithOrderByCreateTime bool + Ascending bool +} + +func getDefaultOptions() options { + return options{} +} + +// WithLimit provides an option to provide a limit. Intentionally allowing +// negative integers. If WithLimit < 0, then unlimited results are +// returned. If WithLimit == 0, then default limits are used for results. +func WithLimit(l int) Option { + return func(o *options) error { + o.WithLimit = l + return nil + } +} + +// WithOrderByCreateTime provides an option to specify ordering by the +// CreateTime field. +func WithOrderByCreateTime(ascending bool) Option { + return func(o *options) error { + o.WithOrderByCreateTime = true + o.Ascending = ascending + return nil + } +} diff --git a/internal/host/options_test.go b/internal/host/options_test.go new file mode 100644 index 000000000..b55d9148c --- /dev/null +++ b/internal/host/options_test.go @@ -0,0 +1,34 @@ +package host + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithLimit", func(t *testing.T) { + opts, err := GetOpts(WithLimit(1)) + require.NoError(t, err) + testOpts := getDefaultOptions() + testOpts.WithLimit = 1 + assert.Equal(t, opts, testOpts) + }) + t.Run("WithOrderByCreateTime-desc", func(t *testing.T) { + opts, err := GetOpts(WithOrderByCreateTime(false)) + require.NoError(t, err) + testOpts := getDefaultOptions() + testOpts.WithOrderByCreateTime = true + assert.Equal(t, opts, testOpts) + }) + t.Run("WithOrderByCreateTime-asc", func(t *testing.T) { + opts, err := GetOpts(WithOrderByCreateTime(true)) + require.NoError(t, err) + testOpts := getDefaultOptions() + testOpts.WithOrderByCreateTime = true + testOpts.Ascending = true + assert.Equal(t, opts, testOpts) + }) +} diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go index 37b8652a3..494e478c4 100644 --- a/internal/host/plugin/host_catalog_secret.go +++ b/internal/host/plugin/host_catalog_secret.go @@ -70,7 +70,7 @@ func (c *HostCatalogSecret) SetTableName(n string) { func (c *HostCatalogSecret) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { const op = "plugin.(HostCatalogSecret).encrypt" if len(c.Secret) == 0 { - errors.New(ctx, errors.InvalidParameter, op, "no attributes defined") + return errors.New(ctx, errors.InvalidParameter, op, "no attributes defined") } if err := structwrapping.WrapStruct(ctx, cipher, c.HostCatalogSecret, nil); err != nil { return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt)) diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go index 6b8364824..0d2137c08 100644 --- a/internal/host/plugin/host_catalog_secret_test.go +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -122,6 +122,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "prefix") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"}) require.NoError(t, err) @@ -131,6 +132,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { assert.NoError(t, err) found, err := newHostCatalogSecret(ctx, cat.GetPublicId(), nil) + require.NoError(t, err) assert.NoError(t, rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId())) require.NoError(t, found.decrypt(ctx, databaseWrapper)) require.NoError(t, hcs.decrypt(ctx, databaseWrapper)) diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go index dac4a9a52..ab882498a 100644 --- a/internal/host/plugin/host_catalog_test.go +++ b/internal/host/plugin/host_catalog_test.go @@ -186,7 +186,7 @@ func TestHostCatalog_Create_DuplicateNames(t *testing.T) { require.NoError(t, err) got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) require.NoError(t, err) - w.Create(ctx, got) + require.NoError(t, w.Create(ctx, got)) // Can't create another resource with the same name in the same scope got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index a60c6f5fb..2785fe814 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -16,17 +16,18 @@ type HostSet struct { tableName string `gorm:"-"` } -// NewHostSet creates a new in memory HostSet assigned to catalogId. -// Attributes, name, and description are the only valid options. All other -// options are ignored. +// NewHostSet creates a new in memory HostSet assigned to catalogId. Attributes, +// name, description, and preferred endpoints are the only valid options. All +// other options are ignored. func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, error) { const op = "plugin.NewHostSet" opts := getOpts(opt...) set := &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalogId, - Name: opts.withName, - Description: opts.withDescription, + CatalogId: catalogId, + Name: opts.withName, + Description: opts.withDescription, + PreferredEndpoints: opts.withPreferredEndpoints, }, } @@ -37,6 +38,7 @@ func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, } set.Attributes = attrs } + return set, nil } diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index 7b2ad4129..a5fbdf6f0 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -133,6 +133,22 @@ func TestHostSet_Create(t *testing.T) { }, }, }, + { + name: "valid-with-preferred-endpoints", + args: args{ + catalogId: cat.GetPublicId(), + opts: []Option{ + WithPreferredEndpoints([]string{"cidr:1.2.3.4"}), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: cat.GetPublicId(), + PreferredEndpoints: []string{"cidr:1.2.3.4"}, + Attributes: []byte("{}"), + }, + }, + }, } for _, tt := range tests { diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index 0db20a72a..e854711bd 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -94,12 +95,13 @@ func TestPluginSet_ImmutableFields(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") w := db.New(conn) wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - new := TestSet(t, conn, cat.GetPublicId()) + new := TestSet(t, conn, kmsCache, cat) tests := []struct { name string @@ -125,7 +127,7 @@ func TestPluginSet_ImmutableFields(t *testing.T) { fieldMask: []string{"CreateTime"}, }, { - name: "plugin_host_catalog_id", + name: "host_plugin_catalog_id", update: func() *HostSet { c := new.testCloneHostSet() c.CatalogId = "stc_01234567890" diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index 894ff6ef6..4c3e260d1 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -14,11 +14,11 @@ type Option func(*options) // options = how options are represented type options struct { - withName string - withDescription string - withLimit int - withAttributes map[string]interface{} - withSecrets map[string]interface{} + withName string + withDescription string + withAttributes map[string]interface{} + withSecrets map[string]interface{} + withPreferredEndpoints []string } func getDefaultOptions() options { @@ -57,11 +57,9 @@ func WithSecrets(secrets map[string]interface{}) Option { } } -// WithLimit provides an option to provide a limit. Intentionally allowing -// negative integers. If WithLimit < 0, then unlimited results are -// returned. If WithLimit == 0, then default limits are used for results. -func WithLimit(l int) Option { +// WithPreferredEndpoints provides an optional preferred endpoints field. +func WithPreferredEndpoints(with []string) Option { return func(o *options) { - o.withLimit = l + o.withPreferredEndpoints = with } } diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index 4cda5a9ff..aab698c2a 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -20,10 +20,10 @@ func Test_GetOpts(t *testing.T) { testOpts.withDescription = "test desc" assert.Equal(t, opts, testOpts) }) - t.Run("WithLimit", func(t *testing.T) { - opts := getOpts(WithLimit(5)) + t.Run("WithPreferredEndpoints", func(t *testing.T) { + opts := getOpts(WithPreferredEndpoints([]string{"foo"})) testOpts := getDefaultOptions() - testOpts.withLimit = 5 - assert.Equal(t, opts, testOpts) + testOpts.withPreferredEndpoints = []string{"foo"} + assert.EqualValues(t, opts, testOpts) }) } diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index 5c009df96..09508d1a6 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -6,6 +6,7 @@ package plugin import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" ) @@ -24,7 +25,7 @@ type Repository struct { // only be used for one transaction and it is not safe for concurrent go // routines to access it. WithLimit option is used as a repo wide default // limit applied to all ListX methods. -func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) (*Repository, error) { const op = "plugin.NewRepository" switch { case r == nil: @@ -35,16 +36,19 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") } - opts := getOpts(opt...) - if opts.withLimit == 0 { + opts, err := host.GetOpts(opt...) + if err != nil { + return nil, errors.WrapDeprecated(err, op) + } + if opts.WithLimit == 0 { // zero signals the boundary defaults should be used. - opts.withLimit = db.DefaultLimit + opts.WithLimit = db.DefaultLimit } return &Repository{ reader: r, writer: w, kms: kms, - defaultLimit: opts.withLimit, + defaultLimit: opts.WithLimit, }, nil } diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index fe6daa029..269d67c2c 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" @@ -75,7 +76,9 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt if err != nil { return nil, errors.Wrap(ctx, err, op) } - hcSecret.encrypt(ctx, dbWrapper) + if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { + return nil, errors.Wrap(ctx, err, op) + } } var newHostCatalog *HostCatalog @@ -146,20 +149,22 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) } // ListCatalogs returns a slice of HostCatalogs for the scope IDs. WithLimit is the only option supported. -func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ...Option) ([]*HostCatalog, error) { +func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ...host.Option) ([]*HostCatalog, error) { const op = "plugin.(Repository).ListCatalogs" if len(scopeIds) == 0 { return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } - opts := getOpts(opt...) + opts, err := host.GetOpts(opt...) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } limit := r.defaultLimit - if opts.withLimit != 0 { + if opts.WithLimit != 0 { // non-zero signals an override of the default limit for the repo. - limit = opts.withLimit + limit = opts.WithLimit } var hostCatalogs []*HostCatalog - err := r.reader.SearchWhere(ctx, &hostCatalogs, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)) - if err != nil { + if err := r.reader.SearchWhere(ctx, &hostCatalogs, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)); err != nil { return nil, errors.Wrap(ctx, err, op) } return hostCatalogs, nil diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 20270c98c..642295750 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -204,7 +204,7 @@ func TestRepository_CreateCatalog(t *testing.T) { dbWrapper, err := kmsCache.GetWrapper(ctx, got.GetScopeId(), kms.KeyPurposeDatabase) require.NoError(t, err) - cSecret.decrypt(ctx, dbWrapper) + require.NoError(t, cSecret.decrypt(ctx, dbWrapper)) assert.Equal(string(tt.wantSecret), string(cSecret.Secret)) }) } diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 3145e7ea3..cc00d3085 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -3,9 +3,14 @@ package plugin import ( "context" "fmt" + "sort" + "strconv" + "strings" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" @@ -65,14 +70,51 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } - var newHostSet *HostSet - _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, - func(_ db.Reader, w db.Writer) error { - newHostSet = s.clone() - err := w.Create(ctx, newHostSet, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_CREATE))) + var preferredEndpoints []interface{} + if s.PreferredEndpoints != nil { + preferredEndpoints = make([]interface{}, 0, len(s.PreferredEndpoints)) + for i, e := range s.PreferredEndpoints { + obj, err := host.NewPreferredEndpoint(ctx, s.PublicId, uint32(i+1), e) if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + preferredEndpoints = append(preferredEndpoints, obj) + } + } + + var returnedHostSet *HostSet + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0, len(preferredEndpoints)+2) + ticket, err := w.GetTicket(s) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + returnedHostSet = s.clone() + + var hsOplogMsg oplog.Message + if err := w.Create(ctx, returnedHostSet, db.NewOplogMsg(&hsOplogMsg)); err != nil { return errors.Wrap(ctx, err, op) } + msgs = append(msgs, &hsOplogMsg) + + if len(preferredEndpoints) > 0 { + peOplogMsgs := make([]*oplog.Message, 0, len(preferredEndpoints)) + if err := w.CreateItems(ctx, preferredEndpoints, db.NewOplogMsgs(&peOplogMsgs)); err != nil { + return err + } + msgs = append(msgs, peOplogMsgs...) + } + + metadata := s.oplog(oplog.OpType_OP_TYPE_CREATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + return nil }, ) @@ -83,49 +125,172 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId))) } - return newHostSet, nil + return returnedHostSet, nil } // LookupSet will look up a host set in the repository and return the host // set. If the host set is not found, it will return nil, nil. // All options are ignored. -func (r *Repository) LookupSet(ctx context.Context, publicId string, _ ...Option) (*HostSet, error) { +func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, error) { const op = "plugin.(Repository).LookupSet" if publicId == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - s := allocHostSet() - s.PublicId = publicId - - err := r.reader.LookupByPublicId(ctx, s) - if errors.IsNotFoundError(err) { - return nil, nil - } + sets, err := r.getSets(ctx, publicId, "", opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", s.PublicId))) + return nil, errors.Wrap(ctx, err, op) } - return s, nil + switch { + case len(sets) == 0: + return nil, nil // not an error to return no rows for a "lookup" + case len(sets) > 1: + return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) + default: + return sets[0], nil + } } // ListSets returns a slice of HostSets for the catalogId. WithLimit is the // only option supported. -func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...Option) ([]*HostSet, error) { +func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host.Option) ([]*HostSet, error) { const op = "plugin.(Repository).ListSets" if catalogId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing catalog id") } - opts := getOpts(opt...) - limit := r.defaultLimit - if opts.withLimit != 0 { - // non-zero signals an override of the default limit for the repo. - limit = opts.withLimit - } - var sets []*HostSet - err := r.reader.SearchWhere(ctx, &sets, "catalog_id = ?", []interface{}{catalogId}, db.WithLimit(limit)) + + sets, err := r.getSets(ctx, "", catalogId, opt...) if err != nil { return nil, errors.Wrap(ctx, err, op) } return sets, nil } + +func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, error) { + const op = "plugin.(Repository).getSets" + const aggregateDelimiter = "|" + const priorityDelimiter = "=" + + if publicId == "" && catalogId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing search criteria: both host set id and catalog id are empty") + } + if publicId != "" && catalogId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "searching for both a host set id and a catalog id is not supported") + } + + opts, err := host.GetOpts(opt...) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + limit := r.defaultLimit + if opts.WithLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.WithLimit + } + + args := make([]interface{}, 0, 1) + var where string + + switch { + case publicId != "": + where, args = "public_id = ?", append(args, publicId) + default: + where, args = "catalog_id = ?", append(args, catalogId) + } + + dbArgs := []db.Option{db.WithLimit(limit)} + + if opts.WithOrderByCreateTime { + if opts.Ascending { + dbArgs = append(dbArgs, db.WithOrder("create_time asc")) + } else { + dbArgs = append(dbArgs, db.WithOrder("create_time")) + } + } + + var aggHostSets []*hostSetAgg + if err := r.reader.SearchWhere(ctx, &aggHostSets, where, args, dbArgs...); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", publicId))) + } + + if len(aggHostSets) == 0 { + return nil, nil + } + + sets := make([]*HostSet, 0, len(aggHostSets)) + for _, agg := range aggHostSets { + hs := allocHostSet() + hs.PublicId = agg.PublicId + hs.CatalogId = agg.CatalogId + hs.Name = agg.Name + hs.Description = agg.Description + hs.CreateTime = agg.CreateTime + hs.UpdateTime = agg.UpdateTime + hs.Version = agg.Version + hs.Attributes = agg.Attributes + if agg.PreferredEndpoints != "" { + eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter) + if len(eps) > 0 { + // We want to protect against someone messing with the DB + // and not panic, so we do a bit of a dance here + var sortErr error + sort.Slice(eps, func(i, j int) bool { + epi := strings.Split(eps[i], priorityDelimiter) + if len(epi) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i])) + return false + } + epj := strings.Split(eps[j], priorityDelimiter) + if len(epj) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j])) + return false + } + indexi, err := strconv.Atoi(epi[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + indexj, err := strconv.Atoi(epj[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + return indexi < indexj + }) + if sortErr != nil { + return nil, sortErr + } + for i, ep := range eps { + // At this point they're in the correct order, but we still + // have to strip off the priority + eps[i] = strings.Split(ep, priorityDelimiter)[1] + } + + hs.PreferredEndpoints = eps + } + } + sets = append(sets, hs) + } + + return sets, nil +} + +// hostSetAgg is a view that aggregates the host set's value objects in to +// string fields delimited with the aggregateDelimiter of "|" +type hostSetAgg struct { + PublicId string `gorm:"primary_key"` + CatalogId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + Version uint32 + Attributes []byte + PreferredEndpoints string +} + +// TableName returns the table name for gorm +func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" } diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index bae081b41..b09655aab 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -10,6 +10,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -60,8 +61,8 @@ func TestRepository_CreateSet(t *testing.T) { name: "invalid-public-id-set", in: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - PublicId: "abcd_OOOOOOOOOO", + CatalogId: catalog.PublicId, + PublicId: "abcd_OOOOOOOOOO", Attributes: attrs, }, }, @@ -80,30 +81,47 @@ func TestRepository_CreateSet(t *testing.T) { name: "valid-no-options", in: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, + CatalogId: catalog.PublicId, Attributes: attrs, }, }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, + CatalogId: catalog.PublicId, Attributes: attrs, }, }, }, + { + name: "valid-preferred-endpoints", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Attributes: attrs, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Attributes: attrs, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + }, + }, + }, { name: "valid-with-name", in: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Name: "test-name-repo", + CatalogId: catalog.PublicId, + Name: "test-name-repo", Attributes: attrs, }, }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Name: "test-name-repo", + CatalogId: catalog.PublicId, + Name: "test-name-repo", Attributes: attrs, }, }, @@ -114,14 +132,14 @@ func TestRepository_CreateSet(t *testing.T) { HostSet: &store.HostSet{ CatalogId: catalog.PublicId, Description: ("test-description-repo"), - Attributes: attrs, + Attributes: attrs, }, }, want: &HostSet{ HostSet: &store.HostSet{ CatalogId: catalog.PublicId, Description: ("test-description-repo"), - Attributes: attrs, + Attributes: attrs, }, }, }, @@ -163,8 +181,8 @@ func TestRepository_CreateSet(t *testing.T) { in := &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Name: "test-name-repo", + CatalogId: catalog.PublicId, + Name: "test-name-repo", Attributes: []byte("{}"), }, } @@ -195,7 +213,7 @@ func TestRepository_CreateSet(t *testing.T) { in := &HostSet{ HostSet: &store.HostSet{ - Name: "test-name-repo", + Name: "test-name-repo", Attributes: []byte("{}"), }, } @@ -234,7 +252,7 @@ func TestRepository_LookupSet(t *testing.T) { plg := hostplg.TestPlugin(t, conn, "lookup", "lookup") catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet := TestSet(t, conn, catalog.PublicId) + hostSet := TestSet(t, conn, kms, catalog) hostSetId, err := newHostSetId(ctx, plg.GetIdPrefix()) require.NoError(t, err) @@ -273,7 +291,9 @@ func TestRepository_LookupSet(t *testing.T) { return } require.NoError(err) - assert.EqualValues(tt.want, got) + if tt.want != nil { + assert.Empty(cmp.Diff(got, tt.want, protocmp.Transform()), "LookupSet(%q) got response %q, wanted %q", tt.in, got, tt.want) + } }) } } @@ -291,15 +311,15 @@ func TestRepository_ListSets(t *testing.T) { catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSets := []*HostSet{ - TestSet(t, conn, catalogA.PublicId), - TestSet(t, conn, catalogA.PublicId), - TestSet(t, conn, catalogA.PublicId), + TestSet(t, conn, kms, catalogA), + TestSet(t, conn, kms, catalogA), + TestSet(t, conn, kms, catalogA), } tests := []struct { name string in string - opts []Option + opts []host.Option want []*HostSet wantIsErr errors.Code }{ @@ -310,7 +330,7 @@ func TestRepository_ListSets(t *testing.T) { { name: "Catalog-with-no-host-sets", in: catalogB.PublicId, - want: []*HostSet{}, + want: nil, }, { name: "Catalog-with-host-sets", @@ -355,13 +375,13 @@ func TestRepository_ListSets_Limits(t *testing.T) { count := 10 var hostSets []*HostSet for i := 0; i < count; i++ { - hostSets = append(hostSets, TestSet(t, conn, catalog.PublicId)) + hostSets = append(hostSets, TestSet(t, conn, kms, catalog)) } tests := []struct { name string - repoOpts []Option - listOpts []Option + repoOpts []host.Option + listOpts []host.Option wantLen int }{ { @@ -370,34 +390,34 @@ func TestRepository_ListSets_Limits(t *testing.T) { }, { name: "With repo limit", - repoOpts: []Option{WithLimit(3)}, + repoOpts: []host.Option{host.WithLimit(3)}, wantLen: 3, }, { name: "With negative repo limit", - repoOpts: []Option{WithLimit(-1)}, + repoOpts: []host.Option{host.WithLimit(-1)}, wantLen: count, }, { name: "With List limit", - listOpts: []Option{WithLimit(3)}, + listOpts: []host.Option{host.WithLimit(3)}, wantLen: 3, }, { name: "With negative List limit", - listOpts: []Option{WithLimit(-1)}, + listOpts: []host.Option{host.WithLimit(-1)}, wantLen: count, }, { name: "With repo smaller than list limit", - repoOpts: []Option{WithLimit(2)}, - listOpts: []Option{WithLimit(6)}, + repoOpts: []host.Option{host.WithLimit(2)}, + listOpts: []host.Option{host.WithLimit(6)}, wantLen: 6, }, { name: "With repo larger than list limit", - repoOpts: []Option{WithLimit(6)}, - listOpts: []Option{WithLimit(2)}, + repoOpts: []host.Option{host.WithLimit(6)}, + listOpts: []host.Option{host.WithLimit(2)}, wantLen: 2, }, } diff --git a/internal/host/plugin/repository_test.go b/internal/host/plugin/repository_test.go index 6ef12a17e..51d03d338 100644 --- a/internal/host/plugin/repository_test.go +++ b/internal/host/plugin/repository_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" ) @@ -21,7 +22,7 @@ func TestRepository_New(t *testing.T) { r db.Reader w db.Writer kms *kms.Kms - opts []Option + opts []host.Option } tests := []struct { @@ -50,7 +51,7 @@ func TestRepository_New(t *testing.T) { r: rw, w: rw, kms: kmsCache, - opts: []Option{WithLimit(5)}, + opts: []host.Option{host.WithLimit(5)}, }, want: &Repository{ reader: rw, diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index c9a0ea5db..17a7cd29c 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -183,9 +183,12 @@ type HostSet struct { // version allows optimistic locking of the resource // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` - // attributes is a jsonb formatted field. + // attributes is a byte field containing marshaled JSON data // @inject_tag: `gorm:"default:null"` Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"` + // preferred_endpoints stores string preference values + // @inject_tag: `gorm:"-"` + PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` } func (x *HostSet) Reset() { @@ -276,6 +279,13 @@ func (x *HostSet) GetAttributes() []byte { return nil } +func (x *HostSet) GetPreferredEndpoints() []string { + if x != nil { + return x.PreferredEndpoints + } + return nil +} + type HostCatalogSecret struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -419,7 +429,7 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x81, 0x03, 0x0a, 0x07, 0x48, 0x6f, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -443,30 +453,33 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x98, 0x02, - 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, + 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, + 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index 5b11d999f..1a860399d 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" @@ -39,29 +40,29 @@ func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...O // TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSet(t *testing.T, conn *gorm.DB, catalogId string, opt ...Option) *HostSet { +func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, opt ...Option) *HostSet { t.Helper() + require := require.New(t) ctx := context.Background() - w := db.New(conn) + rw := db.New(conn) - assert := assert.New(t) - set, err := NewHostSet(ctx, catalogId, opt...) - require.NoError(t, err) - assert.NotNil(set) + repo, err := NewRepository(rw, rw, kmsCache) + require.NoError(err) - cg := allocHostCatalog() - cg.PublicId = catalogId - require.NoError(t, w.LookupByPublicId(ctx, cg)) + set, err := NewHostSet(ctx, hc.PublicId, opt...) + require.NoError(err) + require.NotNil(set) plg := host.NewPlugin("", "") - plg.PublicId = cg.GetPluginId() - require.NoError(t, w.LookupByPublicId(ctx, plg)) + plg.PublicId = hc.GetPluginId() + require.NoError(rw.LookupByPublicId(ctx, plg)) id, err := newHostSetId(ctx, plg.GetIdPrefix()) - assert.NoError(err) - assert.NotEmpty(id) - set.PublicId = id + require.NoError(err) + require.NotEmpty(id) + + set, err = repo.CreateSet(ctx, hc.ScopeId, set, opt...) + require.NoError(err) - require.NoError(t, w.Create(ctx, set)) return set } diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index 7c11e9688..50419aef7 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,6 +36,7 @@ func Test_TestSet(t *testing.T) { assert, require := assert.New(t), require.New(t) conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) require.NotNil(prj) assert.NotEmpty(prj.GetPublicId()) @@ -44,7 +46,7 @@ func Test_TestSet(t *testing.T) { assert.NotEmpty(plg.GetPublicId()) c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) - set := TestSet(t, conn, c.GetPublicId(), WithName("foo"), WithDescription("bar")) + set := TestSet(t, conn, kmsCache, c, WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix"))) assert.Equal("foo", set.GetName()) diff --git a/internal/host/preferred_endpoint.go b/internal/host/preferred_endpoint.go new file mode 100644 index 000000000..9dc32dd5f --- /dev/null +++ b/internal/host/preferred_endpoint.go @@ -0,0 +1,87 @@ +package host + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/store" + "github.com/hashicorp/boundary/internal/libs/endpoint" + "google.golang.org/protobuf/proto" +) + +// defaultPreferredEndpointTableName defines the default table name for a +// PreferredEndpoint +const defaultPreferredEndpointTableName = "host_set_preferred_endpoint" + +// PreferredEndpoint defines an endpoint condition for a host set. +// PreferredEndpoints are value objects of an HostSet, therefore there's no need +// for oplog metadata, since only the HostSet will have metadata because it's +// the root aggregate. +type PreferredEndpoint struct { + *store.PreferredEndpoint + tableName string +} + +// NewPreferredEndpoint creates a new in memory preferred endpoint assigned to a +// HostSet. It supports no options. +func NewPreferredEndpoint(ctx context.Context, hostSetId string, priority uint32, condition string) (*PreferredEndpoint, error) { + const op = "host.NewPreferredEndpoint" + + pe := &PreferredEndpoint{ + PreferredEndpoint: &store.PreferredEndpoint{ + HostSetId: hostSetId, + Priority: priority, + Condition: condition, + }, + } + if err := pe.validate(ctx, op); err != nil { + return nil, err // intentionally not wrapped + } + return pe, nil +} + +// validate the PreferredEndpoint. On success, it will return nil. +func (pe *PreferredEndpoint) validate(ctx context.Context, caller errors.Op) error { + if pe.HostSetId == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing host set id") + } + if pe.Priority < 1 { + return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value") + } + if pe.Condition == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing condition") + } + if _, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder([]string{pe.Condition})); err != nil { + return errors.Wrap(ctx, err, caller) + } + + return nil +} + +// AllocPreferredEndpoint makes an empty one in-memory +func AllocPreferredEndpoint() PreferredEndpoint { + return PreferredEndpoint{ + PreferredEndpoint: &store.PreferredEndpoint{}, + } +} + +// Clone a PreferredEndpoint +func (c *PreferredEndpoint) Clone() *PreferredEndpoint { + cp := proto.Clone(c.PreferredEndpoint) + return &PreferredEndpoint{ + PreferredEndpoint: cp.(*store.PreferredEndpoint), + } +} + +// TableName returns the table name. +func (c *PreferredEndpoint) TableName() string { + if c.tableName != "" { + return c.tableName + } + return defaultPreferredEndpointTableName +} + +// SetTableName sets the table name. +func (c *PreferredEndpoint) SetTableName(n string) { + c.tableName = n +} diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go new file mode 100644 index 000000000..a74310f9d --- /dev/null +++ b/internal/host/preferred_endpoint_test.go @@ -0,0 +1,219 @@ +package host_test + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/plugin" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPreferredEndpoint_Create(t *testing.T) { + t.Parallel() + ctx := context.TODO() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + kmsCache := kms.TestKms(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "create", "create") + catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + set := plugin.TestSet(t, conn, kmsCache, catalog) + + type args struct { + hostSetId string + priority uint32 + condition string + } + tests := []struct { + name string + args args + want *host.PreferredEndpoint + wantErr bool + wantIsErr errors.Code + create bool + wantCreateErr bool + wantCreateIsErr errors.Code + }{ + { + name: "valid", + args: args{ + hostSetId: set.PublicId, + priority: 1, + condition: "cidr:1.2.3.4", + }, + create: true, + want: func() *host.PreferredEndpoint { + want := host.AllocPreferredEndpoint() + want.HostSetId = set.PublicId + want.Condition = "cidr:1.2.3.4" + want.Priority = 1 + return &want + }(), + }, + { + name: "dup", // must follow "valid" test. Url must be be unique for an OidcMethodId + args: args{ + hostSetId: set.PublicId, + priority: 1, + condition: "cidr:1.2.3.4", + }, + create: true, + want: func() *host.PreferredEndpoint { + want := host.AllocPreferredEndpoint() + want.HostSetId = set.PublicId + want.Condition = "cidr:1.2.3.4" + want.Priority = 1 + return &want + }(), + wantCreateErr: true, + wantCreateIsErr: errors.NotUnique, + }, + { + name: "bad-condition", + args: args{ + hostSetId: set.PublicId, + priority: 1, + condition: "foobar:1.2.3.4", + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty-host-set", + args: args{ + hostSetId: "", + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + { + name: "invalid-priority", + args: args{ + hostSetId: set.PublicId, + priority: 0, + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty-condition", + args: args{ + hostSetId: set.PublicId, + priority: 1, + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := host.NewPreferredEndpoint(ctx, tt.args.hostSetId, tt.args.priority, tt.args.condition) + if tt.wantErr { + require.Error(err) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) + return + } + require.NoError(err) + assert.Equal(tt.want, got) + if tt.create { + ctx := context.Background() + err = rw.Create(ctx, got) + if tt.wantCreateErr { + assert.Error(err) + assert.True(errors.Match(errors.T(tt.wantCreateIsErr), err)) + return + } else { + assert.NoError(err) + } + found := host.AllocPreferredEndpoint() + require.NoError(rw.LookupWhere(ctx, &found, "host_set_id = ? and priority = ?", tt.args.hostSetId, tt.args.priority)) + assert.Equal(got, &found) + } + }) + } +} + +func TestPreferredEndpoint_Delete(t *testing.T) { + t.Parallel() + ctx := context.TODO() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + kmsCache := kms.TestKms(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "create", "create") + catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + set := plugin.TestSet(t, conn, kmsCache, catalog) + + peFunc := func(priority uint32, condition string) *host.PreferredEndpoint { + ep, err := host.NewPreferredEndpoint(ctx, set.PublicId, priority, condition) + require.NoError(t, err) + return ep + } + + tests := []struct { + pes []*host.PreferredEndpoint + name string + overrides func(*host.PreferredEndpoint) + wantErr bool + wantRowsDeleted int + }{ + { + name: "valid-single", + pes: []*host.PreferredEndpoint{peFunc(1, "cidr:1.2.3.4")}, + wantErr: false, + wantRowsDeleted: 1, + }, + { + name: "bad-host-set-id", + pes: []*host.PreferredEndpoint{peFunc(1, "cidr:1.2.3.4")}, + overrides: func(pe *host.PreferredEndpoint) { pe.HostSetId = "bad-id" }, + wantErr: false, + wantRowsDeleted: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + var eput *host.PreferredEndpoint + for _, pe := range tt.pes { + cloned := pe.Clone() + if eput == nil { + eput = cloned + } + require.NoError(rw.Create(ctx, &cloned)) + } + if tt.overrides != nil { + tt.overrides(eput) + } + + deletedRows, err := rw.Delete(ctx, &eput, db.WithWhere("host_set_id = ?", eput.HostSetId)) + if tt.wantErr { + require.Error(err) + return + } + + require.NoError(err) + if tt.wantRowsDeleted == 0 { + assert.Equal(tt.wantRowsDeleted, deletedRows) + return + } + require.Equal(tt.wantRowsDeleted, deletedRows) + found := host.AllocPreferredEndpoint() + err = rw.LookupWhere(ctx, &found, "host_set_id = ?", set.PublicId) + assert.True(errors.IsNotFoundError(err)) + }) + } +} diff --git a/internal/host/store/host.pb.go b/internal/host/store/host.pb.go index 25d7e3b0e..a781d8082 100644 --- a/internal/host/store/host.pb.go +++ b/internal/host/store/host.pb.go @@ -199,6 +199,75 @@ func (x *Set) GetCatalogId() string { return "" } +type PreferredEndpoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ID of the host set this belongs to. + // @inject_tag: `gorm:"primary_key"` + HostSetId string `protobuf:"bytes,1,opt,name=host_set_id,json=hostSetId,proto3" json:"host_set_id,omitempty" gorm:"primary_key"` + // The priority order, zero-indexed + // @inject_tag: `gorm:"primary_key"` + Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"` + // The string text of the preference condition + // @inject_tag: `gorm:"not_null"` + Condition string `protobuf:"bytes,3,opt,name=condition,proto3" json:"condition,omitempty" gorm:"not_null"` +} + +func (x *PreferredEndpoint) Reset() { + *x = PreferredEndpoint{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PreferredEndpoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PreferredEndpoint) ProtoMessage() {} + +func (x *PreferredEndpoint) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_store_v1_host_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 PreferredEndpoint.ProtoReflect.Descriptor instead. +func (*PreferredEndpoint) Descriptor() ([]byte, []int) { + return file_controller_storage_host_store_v1_host_proto_rawDescGZIP(), []int{3} +} + +func (x *PreferredEndpoint) GetHostSetId() string { + if x != nil { + return x.HostSetId + } + return "" +} + +func (x *PreferredEndpoint) GetPriority() uint32 { + if x != nil { + return x.Priority + } + return 0 +} + +func (x *PreferredEndpoint) GetCondition() string { + if x != nil { + return x.Condition + } + return "" +} + var File_controller_storage_host_store_v1_host_proto protoreflect.FileDescriptor var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{ @@ -219,11 +288,18 @@ var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{ 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x11, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, + 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -238,11 +314,12 @@ func file_controller_storage_host_store_v1_host_proto_rawDescGZIP() []byte { return file_controller_storage_host_store_v1_host_proto_rawDescData } -var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_controller_storage_host_store_v1_host_proto_goTypes = []interface{}{ - (*Catalog)(nil), // 0: controller.storage.host.store.v1.Catalog - (*Host)(nil), // 1: controller.storage.host.store.v1.Host - (*Set)(nil), // 2: controller.storage.host.store.v1.Set + (*Catalog)(nil), // 0: controller.storage.host.store.v1.Catalog + (*Host)(nil), // 1: controller.storage.host.store.v1.Host + (*Set)(nil), // 2: controller.storage.host.store.v1.Set + (*PreferredEndpoint)(nil), // 3: controller.storage.host.store.v1.PreferredEndpoint } var file_controller_storage_host_store_v1_host_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -294,6 +371,18 @@ func file_controller_storage_host_store_v1_host_proto_init() { return nil } } + file_controller_storage_host_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PreferredEndpoint); 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{ @@ -301,7 +390,7 @@ func file_controller_storage_host_store_v1_host_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_store_v1_host_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/libs/endpoint/preferencer.go b/internal/libs/endpoint/preferencer.go index 17be0893a..44619fd80 100644 --- a/internal/libs/endpoint/preferencer.go +++ b/internal/libs/endpoint/preferencer.go @@ -21,7 +21,7 @@ func NewPreferencer(ctx context.Context, opt ...Option) (*preferencer, error) { const op = "endpoint.NewPreferencer" opts, err := getOpts(opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) } pref := &preferencer{ matchers: opts.withMatchers, diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index add5017ee..28f4d204b 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -43,6 +43,10 @@ message HostSet { // Output only. A list of Hosts in this Host Set. repeated string host_ids = 100 [json_name="host_ids"]; + // An ordered list of endpoint preferences used to choose from among + // multiple possible endpoints for a host. + repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true]; + // The attributes that are applicable for the specific Host Set type. google.protobuf.Struct attributes = 110; diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index e9cb28b64..d5c8b9626 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -78,9 +78,13 @@ message HostSet { // @inject_tag: `gorm:"default:null"` uint32 version = 7; - // attributes is a jsonb formatted field. + // attributes is a byte field containing marshaled JSON data // @inject_tag: `gorm:"default:null"` bytes attributes = 8; + + // preferred_endpoints stores string preference values + // @inject_tag: `gorm:"-"` + repeated string preferred_endpoints = 9; } message HostCatalogSecret { @@ -109,4 +113,4 @@ message HostCatalogSecret { // It must be set. // @inject_tag: `gorm:"not_null"` string key_id = 6; -} +} \ No newline at end of file diff --git a/internal/proto/local/controller/storage/host/store/v1/host.proto b/internal/proto/local/controller/storage/host/store/v1/host.proto index 31a75bf05..e6123406c 100644 --- a/internal/proto/local/controller/storage/host/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/store/v1/host.proto @@ -33,3 +33,17 @@ message Set { // @inject_tag: `gorm:"not_null"` string catalog_id = 2; } + +message PreferredEndpoint { + // The ID of the host set this belongs to. + // @inject_tag: `gorm:"primary_key"` + string host_set_id = 1; + + // The priority order, zero-indexed + // @inject_tag: `gorm:"primary_key"` + uint32 priority = 2; + + // The string text of the preference condition + // @inject_tag: `gorm:"not_null"` + string condition = 3; +} \ No newline at end of file diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 48941bc30..850216571 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/host/plugin" + hostplugin "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -135,8 +135,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.StaticHostRepoFn = func() (*static.Repository, error) { return static.NewRepository(dbase, dbase, c.kms) } - c.PluginHostRepoFn = func() (*plugin.Repository, error) { - return plugin.NewRepository(dbase, dbase, c.kms) + c.PluginHostRepoFn = func() (*hostplugin.Repository, error) { + return hostplugin.NewRepository(dbase, dbase, c.kms) } c.HostPluginRepoFn = func() (*host.Repository, error) { return host.NewRepository(dbase, dbase, c.kms) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 7edd7a1e7..6479c12eb 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -67,7 +67,7 @@ type Service struct { staticRepoFn common.StaticRepoFactory pluginHostRepoFn common.PluginHostRepoFactory - pluginRepoFn common.HostPluginRepoFactory + pluginRepoFn common.HostPluginRepoFactory iamRepoFn common.IamRepoFactory } diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index d4ece68b9..e3a684b1f 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -272,7 +272,7 @@ func TestList(t *testing.T) { var testPluginCatalogs []*pb.HostCatalog name := "test" plg := host.TestPlugin(t, conn, name, name) - for i := 0 ; i < 3; i++ { + for i := 0; i < 3; i++ { hc := plugin.TestCatalog(t, conn, pWithCatalogs.GetPublicId(), plg.GetPublicId()) cat := &pb.HostCatalog{ Id: hc.GetPublicId(), @@ -306,7 +306,7 @@ func TestList(t *testing.T) { name = "different" plg = host.TestPlugin(t, conn, name, name) - for i := 0 ; i < 3; i++ { + for i := 0; i < 3; i++ { hc := plugin.TestCatalog(t, conn, pWithOtherCatalogs.GetPublicId(), plg.GetPublicId()) wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{ Id: hc.GetPublicId(), diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 84965f865..f4498fa65 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" + "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/controller/auth" @@ -80,6 +81,10 @@ func NewService(staticRepoFn common.StaticRepoFactory, pluginRepoFn common.Plugi } func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest) (*pbs.ListHostSetsResponse, error) { + return s.ListHostSetsWithOptions(ctx, req) +} + +func (s Service) ListHostSetsWithOptions(ctx context.Context, req *pbs.ListHostSetsRequest, opt ...host.Option) (*pbs.ListHostSetsResponse, error) { if err := validateListRequest(req); err != nil { return nil, err } @@ -87,7 +92,7 @@ func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest) if authResults.Error != nil { return nil, authResults.Error } - hl, err := s.listFromRepo(ctx, req.GetHostCatalogId()) + hl, err := s.listFromRepo(ctx, req.GetHostCatalogId(), opt...) if err != nil { return nil, err } @@ -177,7 +182,7 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetRequest) (*pbs.CreateHostSetResponse, error) { const op = "host_sets.(Service).CreateHostSet" - if err := validateCreateRequest(req); err != nil { + if err := validateCreateRequest(ctx, req); err != nil { return nil, err } _, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create) @@ -218,7 +223,7 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetRequest) (*pbs.UpdateHostSetResponse, error) { const op = "host_sets.(Service).UpdateHostSet" - if err := validateUpdateRequest(req); err != nil { + if err := validateUpdateRequest(ctx, req); err != nil { return nil, err } cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update) @@ -444,6 +449,9 @@ func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, it hSet = out case plugin.Subtype: h, err := toStoragePluginSet(ctx, catalogId, item) + if err != nil { + return nil, err + } repo, err := s.pluginRepoFn() if err != nil { return nil, err @@ -505,7 +513,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, return rows > 0, nil } -func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Set, error) { +func (s Service) listFromRepo(ctx context.Context, catalogId string, opt ...host.Option) ([]host.Set, error) { const op = "host_sets.(Service).listFromRepo" var sets []host.Set switch host.SubtypeFromId(catalogId) { @@ -526,7 +534,7 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Set if err != nil { return nil, err } - sl, err := repo.ListSets(ctx, catalogId) + sl, err := repo.ListSets(ctx, catalogId, opt...) if err != nil { return nil, errors.Wrap(ctx, err, op) } @@ -742,9 +750,12 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler } } - if outputFields.Has(globals.AttributesField) { - switch h := in.(type) { - case *plugin.HostSet: + switch h := in.(type) { + case *plugin.HostSet: + if outputFields.Has(globals.PreferredEndpointsField) { + out.PreferredEndpoints = h.GetPreferredEndpoints() + } + if outputFields.Has(globals.AttributesField) { attrs := map[string]interface{}{} err := json.Unmarshal(h.Attributes, &attrs) if err != nil { @@ -790,6 +801,9 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) if item.GetAttributes() != nil { opts = append(opts, plugin.WithAttributes(item.GetAttributes().AsMap())) } + if item.GetPreferredEndpoints() != nil { + opts = append(opts, plugin.WithPreferredEndpoints(item.GetPreferredEndpoints())) + } hs, err := plugin.NewHostSet(ctx, catalogId, opts...) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) @@ -814,7 +828,7 @@ func validateGetRequest(req *pbs.GetHostSetRequest) error { return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, pluginPrefix) } -func validateCreateRequest(req *pbs.CreateHostSetRequest) error { +func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) error { return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { badFields := map[string]string{} pluginPrefix := plugin.HostCatalogPrefix @@ -826,6 +840,12 @@ func validateCreateRequest(req *pbs.CreateHostSetRequest) error { if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) { badFields["host_catalog_id"] = "The field is incorrectly formatted." } + if len(req.GetItem().GetPreferredEndpoints()) > 0 { + _, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(req.GetItem().GetPreferredEndpoints())) + if err != nil { + badFields[globals.PreferredEndpointsField] = fmt.Errorf("Error parsing preferred endpoints: %w.", err).Error() + } + } switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) { case static.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { @@ -850,17 +870,23 @@ func validateCreateRequest(req *pbs.CreateHostSetRequest) error { }) } -func validateUpdateRequest(req *pbs.UpdateHostSetRequest) error { +func validateUpdateRequest(ctx context.Context, req *pbs.UpdateHostSetRequest) error { return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string { badFields := map[string]string{} + if len(req.GetItem().GetPreferredEndpoints()) > 0 { + _, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(req.GetItem().GetPreferredEndpoints())) + if err != nil { + badFields[globals.PreferredEndpointsField] = fmt.Errorf("Error parsing preferred endpoints: %w.", err).Error() + } + } switch host.SubtypeFromId(req.GetId()) { case static.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { - badFields["type"] = "Cannot modify the resource type." + badFields[globals.TypeField] = "Cannot modify the resource type." } } return badFields - }, static.HostSetPrefix) + }, static.HostSetPrefix, plugin.HostSetPrefix) } func validateDeleteRequest(req *pbs.DeleteHostSetRequest) error { diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 355e709c8..7722b5efe 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -11,11 +11,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" @@ -150,20 +151,22 @@ func TestGet_Plugin(t *testing.T) { } name := "test" - plg := host.TestPlugin(t, conn, name, name) + prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} + plg := hostplugin.TestPlugin(t, conn, name, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, hc.GetPublicId()) + hs := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(prefEndpoints)) toMerge := &pbs.GetHostSetRequest{} pHost := &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Id: hs.GetPublicId(), - CreatedTime: hs.CreateTime.GetTimestamp(), - UpdatedTime: hs.UpdateTime.GetTimestamp(), - Type: name, - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, - AuthorizedActions: testAuthorizedActions, + HostCatalogId: hc.GetPublicId(), + Id: hs.GetPublicId(), + CreatedTime: hs.CreateTime.GetTimestamp(), + UpdatedTime: hs.UpdateTime.GetTimestamp(), + Type: name, + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + PreferredEndpoints: prefEndpoints, + AuthorizedActions: testAuthorizedActions, } cases := []struct { @@ -179,7 +182,7 @@ func TestGet_Plugin(t *testing.T) { }, { name: "Get a non existing Host Set", - req: &pbs.GetHostSetRequest{Id: static.HostSetPrefix + "_DoesntExis"}, + req: &pbs.GetHostSetRequest{Id: plugin.HostSetPrefix + "_DoesntExis"}, res: nil, err: handlers.ApiErrorWithCode(codes.NotFound), }, @@ -191,7 +194,7 @@ func TestGet_Plugin(t *testing.T) { }, { name: "space in id", - req: &pbs.GetHostSetRequest{Id: static.HostPrefix + "_1 23456789"}, + req: &pbs.GetHostSetRequest{Id: plugin.HostSetPrefix + "_1 23456789"}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, @@ -339,22 +342,24 @@ func TestList_Plugin(t *testing.T) { return plugin.NewRepository(rw, rw, kms) } name := "test" - plg := host.TestPlugin(t, conn, name, name) + plg := hostplugin.TestPlugin(t, conn, name, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + preferredEndpoints := []string{"cidr:1.2.3.4", "dns:*.foobar.com"} var wantHs []*pb.HostSet for i := 0; i < 10; i++ { - h := plugin.TestSet(t, conn, hc.GetPublicId()) + h := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(preferredEndpoints)) wantHs = append(wantHs, &pb.HostSet{ - Id: h.GetPublicId(), - HostCatalogId: h.GetCatalogId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, - CreatedTime: h.GetCreateTime().GetTimestamp(), - UpdatedTime: h.GetUpdateTime().GetTimestamp(), - Version: h.GetVersion(), - Type: name, - AuthorizedActions: testAuthorizedActions, + Id: h.GetPublicId(), + HostCatalogId: h.GetCatalogId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + CreatedTime: h.GetCreateTime().GetTimestamp(), + UpdatedTime: h.GetUpdateTime().GetTimestamp(), + Version: h.GetVersion(), + Type: name, + AuthorizedActions: testAuthorizedActions, + PreferredEndpoints: preferredEndpoints, }) } @@ -397,7 +402,7 @@ func TestList_Plugin(t *testing.T) { require.NoError(err, "Couldn't create new host set service.") // Test with non-anon user - got, gErr := s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) + got, gErr := s.ListHostSetsWithOptions(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req, host.WithOrderByCreateTime(false)) if tc.err != nil { require.Error(gErr) assert.True(errors.Is(gErr, tc.err), "ListHostSets(%q) got error %v, wanted %v", tc.req, gErr, tc.err) @@ -699,7 +704,7 @@ func TestCreate_Plugin(t *testing.T) { return plugin.NewRepository(rw, rw, kms) } name := "test" - plg := host.TestPlugin(t, conn, name, name) + plg := hostplugin.TestPlugin(t, conn, name, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) testAttrs, err := structpb.NewStruct(map[string]interface{}{ @@ -724,6 +729,8 @@ func TestCreate_Plugin(t *testing.T) { }) require.NoError(t, err) + prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} + defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime() cases := []struct { @@ -774,6 +781,39 @@ func TestCreate_Plugin(t *testing.T) { }, }, }, + { + name: "With Preferred Endpoints", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + PreferredEndpoints: prefEndpoints, + }}, + res: &pbs.CreateHostSetResponse{ + Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + PreferredEndpoints: prefEndpoints, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Bad Preferred Endpoints", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: name, + PreferredEndpoints: append(prefEndpoints, "foobar:1.2.3.4"), + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, { name: "Create with mismatched type/name", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index db043e18f..d28371373 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -1731,7 +1731,7 @@ func validateSetSetsRequest(req *pbs.SetTargetHostSetsRequest) error { badFields[globals.VersionField] = "Required field." } for _, id := range req.GetHostSetIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSetIdsField] = fmt.Sprintf("Incorrectly formatted host set identifier %q.", id) break @@ -1755,7 +1755,7 @@ func validateRemoveSetsRequest(req *pbs.RemoveTargetHostSetsRequest) error { badFields[globals.HostSetIdsField] = "Must be non-empty." } for _, id := range req.GetHostSetIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSetIdsField] = fmt.Sprintf("Incorrectly formatted host set identifier %q.", id) break @@ -1779,7 +1779,7 @@ func validateAddHostSourcesRequest(req *pbs.AddTargetHostSourcesRequest) error { badFields[globals.HostSourceIdsField] = "Must be non-empty." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break @@ -1800,7 +1800,7 @@ func validateSetHostSourcesRequest(req *pbs.SetTargetHostSourcesRequest) error { badFields[globals.VersionField] = "Required field." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break @@ -1824,7 +1824,7 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e badFields[globals.HostSourceIdsField] = "Must be non-empty." } for _, id := range req.GetHostSourceIds() { - if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && + if !handlers.ValidId(handlers.Id(id), static.HostSetPrefix) && !strings.HasPrefix(id, fmt.Sprintf("%s_", plugin.HostSetPrefix)) { badFields[globals.HostSourceIdsField] = fmt.Sprintf("Incorrectly formatted host source identifier %q.", id) break diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index b776a69b0..ac7d36e02 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -983,7 +983,7 @@ func TestAddTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) addCases := []struct { name string @@ -1115,7 +1115,7 @@ func TestSetTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) setCases := []struct { name string @@ -1241,7 +1241,7 @@ func TestRemoveTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) removeCases := []struct { name string @@ -1379,7 +1379,7 @@ func TestAddTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) addCases := []struct { name string @@ -1511,7 +1511,7 @@ func TestSetTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) setCases := []struct { name string @@ -1631,7 +1631,7 @@ func TestRemoveTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, pluginHc.GetPublicId()) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc) removeCases := []struct { name string diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index 04b03c2b1..153dbdf1f 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -52,6 +52,9 @@ type HostSet struct { Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty"` // Output only. A list of Hosts in this Host Set. HostIds []string `protobuf:"bytes,100,rep,name=host_ids,proto3" json:"host_ids,omitempty"` + // An ordered list of endpoint preferences used to choose from among + // multiple possible endpoints for a host. + PreferredEndpoints []string `protobuf:"bytes,101,rep,name=preferred_endpoints,proto3" json:"preferred_endpoints,omitempty"` // The attributes that are applicable for the specific Host Set type. Attributes *structpb.Struct `protobuf:"bytes,110,opt,name=attributes,proto3" json:"attributes,omitempty"` // Output only. The available actions on this resource for this user. @@ -160,6 +163,13 @@ func (x *HostSet) GetHostIds() []string { return nil } +func (x *HostSet) GetPreferredEndpoints() []string { + if x != nil { + return x.PreferredEndpoints + } + return nil +} + func (x *HostSet) GetAttributes() *structpb.Struct { if x != nil { return x.Attributes @@ -194,7 +204,7 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe8, 0x04, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, @@ -226,19 +236,23 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x68, - 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, - 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, - 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, + 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, + 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, + 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From bbfef2802fd34ce8260e0101bc07e83dd3768f4c Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 21 Sep 2021 09:18:49 -0700 Subject: [PATCH 18/71] Placeholder Plugin Providers for Repos and added testPlugin struct (#1532) * Create minimalistic plugin map and test plugin. * Pass secrets and attributes to the plugin when creating catalogs or sets. Add tests for CreateSet and CreateCatalog repo methods. * make fmt and extract out getPrivateCatalog and getCatalog helper methods. * Exporting TestPluginServer so it can be used in the handlers. * Keep separate persisted data from host catalogs lookups which facilitate passing it onto the plugins. * Add TODO to merge get Catalog and get Persisted data. * Adding tests for unimplemented being returned by the plugin. * Use GetFields() instead of AsMap() to confirm attributes should be populated. Make clone consistent between catalog and sets. Co-authored-by: Jeff Mitchell --- .../postgres/17/04_plugin_host.up.sql | 8 +- internal/db/schema/postgres_migration.gen.go | 10 +- internal/host/plugin/host_catalog.go | 31 ++-- internal/host/plugin/host_catalog_secret.go | 6 +- .../host/plugin/host_catalog_secret_test.go | 21 ++- internal/host/plugin/host_catalog_test.go | 17 +- internal/host/plugin/host_set.go | 21 +-- internal/host/plugin/host_set_test.go | 27 ++-- internal/host/plugin/immutable_fields_test.go | 5 +- internal/host/plugin/options.go | 14 +- internal/host/plugin/repository.go | 16 +- .../host/plugin/repository_host_catalog.go | 114 ++++++++++++-- .../plugin/repository_host_catalog_test.go | 146 ++++++++++++++---- internal/host/plugin/repository_host_set.go | 56 ++++++- .../host/plugin/repository_host_set_test.go | 100 ++++++++++-- internal/host/plugin/repository_test.go | 71 ++++++--- internal/host/plugin/store/host.pb.go | 10 +- internal/host/plugin/testing.go | 66 +++++++- internal/host/plugin/testing_test.go | 3 +- internal/host/preferred_endpoint_test.go | 9 +- internal/plugin/host/store/plugin.pb.go | 2 +- internal/plugin/store/plugin.pb.go | 2 +- .../storage/host/plugin/store/v1/host.proto | 4 +- internal/servers/controller/controller.go | 3 +- .../host_catalogs/host_catalog_service.go | 17 +- .../host_catalog_service_test.go | 27 ++-- .../handlers/host_sets/host_set_service.go | 15 +- .../host_sets/host_set_service_test.go | 48 +++--- .../handlers/targets/target_service_test.go | 25 ++- sdk/pbs/plugin/host_plugin_service.pb.go | 2 +- 30 files changed, 669 insertions(+), 227 deletions(-) diff --git a/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql b/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql index 3b4323de7..d3b80e48c 100644 --- a/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql +++ b/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql @@ -54,9 +54,7 @@ begin; create_time wt_timestamp, update_time wt_timestamp, version wt_version, - attributes bytea not null - constraint attributes_must_not_be_empty - check(length(attributes) > 0), + attributes bytea not null, constraint host_catalog_fkey foreign key (scope_id, public_id) references host_catalog (scope_id, public_id) @@ -126,9 +124,7 @@ begin; create_time wt_timestamp, update_time wt_timestamp, version wt_version, - attributes bytea not null - constraint attributes_must_not_be_empty - check(length(attributes) > 0), + attributes bytea not null, constraint host_plugin_set_catalog_id_name_uq unique(catalog_id, name), constraint host_set_fkey diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 81c48f577..1c3d02a05 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -7742,9 +7742,7 @@ alter table wh_session_connection_accumulating_fact create_time wt_timestamp, update_time wt_timestamp, version wt_version, - attributes bytea not null - constraint attributes_must_not_be_empty - check(length(attributes) > 0), + attributes bytea not null, constraint host_catalog_fkey foreign key (scope_id, public_id) references host_catalog (scope_id, public_id) @@ -7814,9 +7812,7 @@ alter table wh_session_connection_accumulating_fact create_time wt_timestamp, update_time wt_timestamp, version wt_version, - attributes bytea not null - constraint attributes_must_not_be_empty - check(length(attributes) > 0), + attributes bytea not null, constraint host_plugin_set_catalog_id_name_uq unique(catalog_id, name), constraint host_set_fkey @@ -7864,7 +7860,7 @@ create table host_set_preferred_endpoint ( constraint priority_must_be_greater_than_zero check(priority > 0), condition text not null - constraint condition_must_not_be_empty + constraint condition_must_not_be_too_short check(length(trim(condition)) > 4) -- minimum is 'dns:*' constraint condition_must_not_be_too_long check(length(trim(condition)) < 255) diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index f64c5f743..437b5ecb4 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -5,12 +5,12 @@ package plugin import ( "context" - "encoding/json" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/oplog" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) // A HostCatalog contains plugin host sets. It is owned by @@ -19,7 +19,7 @@ type HostCatalog struct { *store.HostCatalog tableName string `gorm:"-"` - secrets map[string]interface{} `gorm:"-"` + secrets *structpb.Struct `gorm:"-"` } // NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId @@ -28,23 +28,22 @@ type HostCatalog struct { func NewHostCatalog(ctx context.Context, scopeId, pluginId string, opt ...Option) (*HostCatalog, error) { const op = "plugin.NewHostCatalog" opts := getOpts(opt...) + + attrs, err := proto.Marshal(opts.withAttributes) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) + } + hc := &HostCatalog{ HostCatalog: &store.HostCatalog{ ScopeId: scopeId, PluginId: pluginId, Name: opts.withName, Description: opts.withDescription, + Attributes: attrs, }, secrets: opts.withSecrets, } - - if opts.withAttributes != nil { - attrs, err := json.Marshal(opts.withAttributes) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) - } - hc.Attributes = attrs - } return hc, nil } @@ -58,16 +57,16 @@ func allocHostCatalog() *HostCatalog { // secret. The secret shallow copied. func (c *HostCatalog) clone() *HostCatalog { cp := proto.Clone(c.HostCatalog) + newSecret := proto.Clone(c.secrets) hc := &HostCatalog{ HostCatalog: cp.(*store.HostCatalog), + secrets: newSecret.(*structpb.Struct), } - if c.secrets != nil { - newSecret := make(map[string]interface{}, len(c.secrets)) - for k, v := range c.secrets { - newSecret[k] = v - } - hc.secrets = newSecret + // proto.Clone will convert slices with length and capacity of 0 to nil. + // Fix this since gorm treats empty slices differently than nil. + if c.Attributes != nil && len(c.Attributes) == 0 && hc.Attributes == nil { + hc.Attributes = []byte{} } return hc } diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go index 494e478c4..83916ec02 100644 --- a/internal/host/plugin/host_catalog_secret.go +++ b/internal/host/plugin/host_catalog_secret.go @@ -2,7 +2,6 @@ package plugin import ( "context" - "encoding/json" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -11,6 +10,7 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/structwrapping" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) // HostCatalogSecret contains the encrypted secret for a host catalog. @@ -22,7 +22,7 @@ type HostCatalogSecret struct { // newHostCatalogSecret creates an in memory host catalog secret. // All options are ignored. -func newHostCatalogSecret(ctx context.Context, catalogId string, secret map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) { +func newHostCatalogSecret(ctx context.Context, catalogId string, secret *structpb.Struct, _ ...Option) (*HostCatalogSecret, error) { const op = "plugin.newHostCatlogSecret" hcs := &HostCatalogSecret{ HostCatalogSecret: &store.HostCatalogSecret{ @@ -31,7 +31,7 @@ func newHostCatalogSecret(ctx context.Context, catalogId string, secret map[stri } if secret != nil { - attrs, err := json.Marshal(secret) + attrs, err := proto.Marshal(secret) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) } diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go index 0d2137c08..f6559caf5 100644 --- a/internal/host/plugin/host_catalog_secret_test.go +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -2,7 +2,6 @@ package plugin import ( "context" - "encoding/json" "testing" "github.com/google/go-cmp/cmp" @@ -14,7 +13,9 @@ import ( "github.com/hashicorp/boundary/internal/plugin/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" ) func TestHostCatalogSecret_New(t *testing.T) { @@ -28,7 +29,7 @@ func TestHostCatalogSecret_New(t *testing.T) { type args struct { catalogId string - attrs map[string]interface{} + attrs *structpb.Struct } tests := []struct { @@ -64,13 +65,19 @@ func TestHostCatalogSecret_New(t *testing.T) { name: "valid", args: args{ catalogId: cat.GetPublicId(), - attrs: map[string]interface{}{"foo": "bar"}, + attrs: func() *structpb.Struct { + st, err := structpb.NewStruct(map[string]interface{}{"foo": "bar"}) + require.NoError(t, err) + return st + }(), }, want: &HostCatalogSecret{ HostCatalogSecret: &store.HostCatalogSecret{ CatalogId: cat.GetPublicId(), Secret: func() []byte { - b, err := json.Marshal(map[string]interface{}{"foo": "bar"}) + st, err := structpb.NewStruct(map[string]interface{}{"foo": "bar"}) + require.NoError(t, err) + b, err := proto.Marshal(st) require.NoError(t, err) return b }(), @@ -124,7 +131,8 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) require.NoError(t, err) - hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"}) + hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), + &structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}) require.NoError(t, err) assert.NoError(t, hcs.encrypt(ctx, databaseWrapper)) q, v := hcs.insertQuery() @@ -141,7 +149,8 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { assert.Empty(t, cmp.Diff(hcs, found, protocmp.Transform())) // Update the secret and see the value updated. - updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"updated": "value"}) + updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(), + &structpb.Struct{Fields: map[string]*structpb.Value{"updated": structpb.NewStringValue("value")}}) require.NoError(t, err) assert.NoError(t, updated.encrypt(ctx, databaseWrapper)) q, v = updated.insertQuery() diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go index ab882498a..761dacb5a 100644 --- a/internal/host/plugin/host_catalog_test.go +++ b/internal/host/plugin/host_catalog_test.go @@ -2,7 +2,6 @@ package plugin import ( "context" - "encoding/json" "testing" "github.com/google/go-cmp/cmp" @@ -13,7 +12,9 @@ import ( "github.com/hashicorp/boundary/internal/plugin/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" ) func TestHostCatalog_Create(t *testing.T) { @@ -45,7 +46,7 @@ func TestHostCatalog_Create(t *testing.T) { want: &HostCatalog{ HostCatalog: &store.HostCatalog{ PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantCreateErr: true, @@ -59,7 +60,7 @@ func TestHostCatalog_Create(t *testing.T) { want: &HostCatalog{ HostCatalog: &store.HostCatalog{ ScopeId: prj.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantCreateErr: true, @@ -74,7 +75,7 @@ func TestHostCatalog_Create(t *testing.T) { HostCatalog: &store.HostCatalog{ PluginId: plg.GetPublicId(), ScopeId: prj.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -92,7 +93,7 @@ func TestHostCatalog_Create(t *testing.T) { PluginId: plg.GetPublicId(), ScopeId: prj.GetPublicId(), Name: "test-name", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -110,7 +111,7 @@ func TestHostCatalog_Create(t *testing.T) { PluginId: plg.GetPublicId(), ScopeId: prj.GetPublicId(), Description: "test-description", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -120,7 +121,7 @@ func TestHostCatalog_Create(t *testing.T) { pluginId: plg.GetPublicId(), scopeId: prj.GetPublicId(), opts: []Option{ - WithAttributes(map[string]interface{}{"foo": "bar"}), + WithAttributes(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}), }, }, want: func() *HostCatalog { @@ -131,7 +132,7 @@ func TestHostCatalog_Create(t *testing.T) { }, } var err error - hc.Attributes, err = json.Marshal(map[string]interface{}{"foo": "bar"}) + hc.Attributes, err = proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}) require.NoError(t, err) return hc }(), diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index 2785fe814..e0703c83a 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -2,7 +2,6 @@ package plugin import ( "context" - "encoding/json" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" @@ -22,23 +21,21 @@ type HostSet struct { func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, error) { const op = "plugin.NewHostSet" opts := getOpts(opt...) + attrs, err := proto.Marshal(opts.withAttributes) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) + } + set := &HostSet{ HostSet: &store.HostSet{ CatalogId: catalogId, Name: opts.withName, Description: opts.withDescription, + Attributes: attrs, PreferredEndpoints: opts.withPreferredEndpoints, }, } - if opts.withAttributes != nil { - attrs, err := json.Marshal(opts.withAttributes) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) - } - set.Attributes = attrs - } - return set, nil } @@ -64,9 +61,13 @@ func allocHostSet() *HostSet { func (s *HostSet) clone() *HostSet { cp := proto.Clone(s.HostSet) - return &HostSet{ + hs := &HostSet{ HostSet: cp.(*store.HostSet), } + if s.Attributes != nil && len(s.Attributes) == 0 && hs.Attributes == nil { + hs.Attributes = []byte{} + } + return hs } func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata { diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index a5fbdf6f0..ecfce316a 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -10,6 +10,8 @@ import ( "github.com/hashicorp/boundary/internal/plugin/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) func TestHostSet_Create(t *testing.T) { @@ -37,7 +39,7 @@ func TestHostSet_Create(t *testing.T) { catalogId: "", }, want: &HostSet{HostSet: &store.HostSet{ - Attributes: []byte("{}"), + Attributes: []byte{}, }}, wantErr: true, }, @@ -49,7 +51,7 @@ func TestHostSet_Create(t *testing.T) { want: &HostSet{ HostSet: &store.HostSet{ CatalogId: cat.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -65,7 +67,7 @@ func TestHostSet_Create(t *testing.T) { HostSet: &store.HostSet{ CatalogId: cat.GetPublicId(), Name: "test-name", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -81,7 +83,7 @@ func TestHostSet_Create(t *testing.T) { HostSet: &store.HostSet{ CatalogId: cat.GetPublicId(), Name: "test-name", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantErr: true, @@ -98,7 +100,7 @@ func TestHostSet_Create(t *testing.T) { HostSet: &store.HostSet{ CatalogId: cat2.GetPublicId(), Name: "test-name", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -114,7 +116,7 @@ func TestHostSet_Create(t *testing.T) { HostSet: &store.HostSet{ CatalogId: cat.GetPublicId(), Description: "test-description", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -123,13 +125,18 @@ func TestHostSet_Create(t *testing.T) { args: args{ catalogId: cat.GetPublicId(), opts: []Option{ - WithAttributes(map[string]interface{}{"foo": "bar"}), + WithAttributes(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}), }, }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: cat.GetPublicId(), - Attributes: []byte(`{"foo":"bar"}`), + CatalogId: cat.GetPublicId(), + Attributes: func() []byte { + st := &structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}} + b, err := proto.Marshal(st) + require.NoError(t, err) + return b + }(), }, }, }, @@ -145,7 +152,7 @@ func TestHostSet_Create(t *testing.T) { HostSet: &store.HostSet{ CatalogId: cat.GetPublicId(), PreferredEndpoints: []string{"cidr:1.2.3.4"}, - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index e854711bd..641c1b575 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -101,7 +102,9 @@ func TestPluginSet_ImmutableFields(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test", "prefix") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - new := TestSet(t, conn, kmsCache, cat) + new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + }) tests := []struct { name string diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index 4c3e260d1..b711478cb 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -1,5 +1,7 @@ package plugin +import "google.golang.org/protobuf/types/known/structpb" + // getOpts - iterate the inbound Options and return a struct func getOpts(opt ...Option) options { opts := getDefaultOptions() @@ -16,16 +18,14 @@ type Option func(*options) type options struct { withName string withDescription string - withAttributes map[string]interface{} - withSecrets map[string]interface{} + withAttributes *structpb.Struct + withSecrets *structpb.Struct withPreferredEndpoints []string } func getDefaultOptions() options { return options{ - withDescription: "", - withName: "", - withAttributes: make(map[string]interface{}), + withAttributes: &structpb.Struct{}, } } @@ -44,14 +44,14 @@ func WithName(name string) Option { } // WithAttributes provides an optional attributes field. -func WithAttributes(attrs map[string]interface{}) Option { +func WithAttributes(attrs *structpb.Struct) Option { return func(o *options) { o.withAttributes = attrs } } // WithSecrets provides an optional secrets field. -func WithSecrets(secrets map[string]interface{}) Option { +func WithSecrets(secrets *structpb.Struct) Option { return func(o *options) { o.withSecrets = secrets } diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index 09508d1a6..5755b3db3 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" ) // A Repository stores and retrieves the persistent types in the plugin @@ -16,6 +17,11 @@ type Repository struct { reader db.Reader writer db.Writer kms *kms.Kms + + // plugins is a map from plugin resource id to host plugin client. + // TODO: When we are using go-plugin change from using a plgpb.HostPluginServiceServer + // to the client. + plugins map[string]plgpb.HostPluginServiceServer // defaultLimit provides a default for limiting the number of results // returned from the repo defaultLimit int @@ -25,7 +31,7 @@ type Repository struct { // only be used for one transaction and it is not safe for concurrent go // routines to access it. WithLimit option is used as a repo wide default // limit applied to all ListX methods. -func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) (*Repository, error) { +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceServer, opt ...host.Option) (*Repository, error) { const op = "plugin.NewRepository" switch { case r == nil: @@ -34,6 +40,8 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) ( return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer") case kms == nil: return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") + case plgm == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "plgm") } opts, err := host.GetOpts(opt...) @@ -45,10 +53,16 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) ( opts.WithLimit = db.DefaultLimit } + plgs := make(map[string]plgpb.HostPluginServiceServer, len(plgm)) + for k, v := range plgm { + plgs[k] = v + } + return &Repository{ reader: r, writer: w, kms: kms, + plugins: plgs, defaultLimit: opts.WithLimit, }, nil } diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 269d67c2c..aac135385 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -10,6 +10,12 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) // CreateCatalog inserts c into the repository and returns a new @@ -44,7 +50,6 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } c = c.clone() - // TODO: Capture this in a plugin manager and call the plugin's OnCreateCatalog function plg := hostplg.NewPlugin("", "") plg.PublicId = c.PluginId if err := r.reader.LookupByPublicId(ctx, plg); err != nil { @@ -60,27 +65,41 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } c.PublicId = id - oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + plgClient, ok := r.plugins[plg.GetPublicId()] + if !ok || plgClient == nil { + return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", plg.GetPluginName())) } - dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) + plgHc, err := toPluginCatalog(ctx, c) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + return nil, errors.Wrap(ctx, err, op) + } + plgResp, err := plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc}) + if err != nil { + if status.Code(err) != codes.Unimplemented { + return nil, errors.Wrap(ctx, err, op) + } } var hcSecret *HostCatalogSecret - if c.secrets != nil { - // TODO: Create the secret using the returned value from the call to the plugin. - hcSecret, err = newHostCatalogSecret(ctx, id, c.secrets) + if plgResp != nil && plgResp.GetPersisted().GetData() != nil { + hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetData()) if err != nil { return nil, errors.Wrap(ctx, err, op) } + dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { return nil, errors.Wrap(ctx, err, op) } } + oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + var newHostCatalog *HostCatalog _, err = r.writer.DoTx( ctx, @@ -137,9 +156,8 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) if id == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - c := allocHostCatalog() - c.PublicId = id - if err := r.reader.LookupByPublicId(ctx, c); err != nil { + c, err := r.getCatalog(ctx, id) + if err != nil { if errors.IsNotFoundError(err) { return nil, nil } @@ -169,3 +187,75 @@ func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt .. } return hostCatalogs, nil } + +// getCatalog retrieves the *HostCatalog with the provided id. If it is not found or there +// is an problem getting it from the database an error is returned instead. +func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, error) { + const op = "plugin.(Repository).getCatalog" + c := allocHostCatalog() + c.PublicId = id + if err := r.reader.LookupByPublicId(ctx, c); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + } + return c, nil +} + +// getPersistedDataForCatalog returns the persisted data for a catalog if +// present. c must have a valid Public Id and Scope Id set. +// TODO: consider merging the functions for getting catalog and persisted data into a view. +func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCatalog) (*plgpb.HostCatalogPersisted, error) { + const op = "plugin.(Repository).getPersistedDataForCatalog" + if c.PublicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty public id") + } + if c.ScopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty scope id") + } + cSecret := allocHostCatalogSecret() + if err := r.reader.LookupWhere(ctx, cSecret, "catalog_id=?", c.GetPublicId()); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op) + } + if cSecret == nil { + return nil, nil + } + dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } + if err := cSecret.decrypt(ctx, dbWrapper); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + data := &structpb.Struct{} + if err := proto.Unmarshal(cSecret.GetSecret(), data); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unmarshaling secret")) + } + return &plgpb.HostCatalogPersisted{Data: data}, nil +} + +// toPluginCatalog returns a host catalog, with it's secret if available, in the format expected +// by the host plugin system. +func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, error) { + const op = "plugin.toPluginCatalog" + if in == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin") + } + hc := &pb.HostCatalog{ + Id: in.GetPublicId(), + ScopeId: in.GetScopeId(), + } + if in.GetAttributes() != nil { + attrs := &structpb.Struct{} + if err := proto.Unmarshal(in.GetAttributes(), attrs); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to unmarshal attributes")) + } + hc.Attributes = attrs + } + if in.secrets != nil { + hc.Secrets = in.secrets + } + return hc, nil +} diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 642295750..9a25af2b7 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" @@ -13,8 +14,12 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" ) func TestRepository_CreateCatalog(t *testing.T) { @@ -24,13 +29,27 @@ func TestRepository_CreateCatalog(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test", "test") + unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented") + + // gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the + // test against the expected values sent to the plugin. + var gotPluginAttrs *structpb.Struct + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{ + OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + gotPluginAttrs = req.GetCatalog().GetAttributes() + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Data: req.GetCatalog().GetSecrets()}}, nil + }, + }, + unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + } tests := []struct { name string in *HostCatalog opts []Option want *HostCatalog - wantSecret []byte + wantSecret *structpb.Struct wantIsErr errors.Code }{ { @@ -47,7 +66,7 @@ func TestRepository_CreateCatalog(t *testing.T) { in: &HostCatalog{ HostCatalog: &store.HostCatalog{ PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantIsErr: errors.InvalidParameter, @@ -57,7 +76,7 @@ func TestRepository_CreateCatalog(t *testing.T) { in: &HostCatalog{ HostCatalog: &store.HostCatalog{ ScopeId: prj.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantIsErr: errors.InvalidParameter, @@ -78,14 +97,31 @@ func TestRepository_CreateCatalog(t *testing.T) { HostCatalog: &store.HostCatalog{ ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, want: &HostCatalog{ HostCatalog: &store.HostCatalog{ ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, + }, + }, + }, + { + name: "valid-unimplemented-plugin", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: unimplementedPlugin.GetPublicId(), + Attributes: []byte{}, + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: unimplementedPlugin.GetPublicId(), + Attributes: []byte{}, }, }, }, @@ -95,7 +131,7 @@ func TestRepository_CreateCatalog(t *testing.T) { HostCatalog: &store.HostCatalog{ ScopeId: prj.GetPublicId(), PluginId: "unknown_plugin", - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, wantIsErr: errors.InvalidParameter, @@ -107,7 +143,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Name: "test-name-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, want: &HostCatalog{ @@ -115,7 +151,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Name: "test-name-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, }, @@ -126,7 +162,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Description: "test-description-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, want: &HostCatalog{ @@ -134,7 +170,36 @@ func TestRepository_CreateCatalog(t *testing.T) { Description: "test-description-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, + }, + }, + }, + { + name: "valid-with-attributes", + in: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: func() []byte { + st, err := structpb.NewStruct(map[string]interface{}{"k1": "foo"}) + require.NoError(t, err) + b, err := proto.Marshal(st) + require.NoError(t, err) + return b + }(), + }, + }, + want: &HostCatalog{ + HostCatalog: &store.HostCatalog{ + ScopeId: prj.GetPublicId(), + PluginId: plg.GetPublicId(), + Attributes: func() []byte { + st, err := structpb.NewStruct(map[string]interface{}{"k1": "foo"}) + require.NoError(t, err) + b, err := proto.Marshal(st) + require.NoError(t, err) + return b + }(), }, }, }, @@ -145,23 +210,35 @@ func TestRepository_CreateCatalog(t *testing.T) { Description: "test-description-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), - }, - secrets: map[string]interface{}{ - "k1": "v1", - "k2": 2, - "k3": nil, + Attributes: []byte{}, }, + secrets: func() *structpb.Struct { + st, err := structpb.NewStruct(map[string]interface{}{ + "k1": "v1", + "k2": 2, + "k3": nil, + }) + require.NoError(t, err) + return st + }(), }, want: &HostCatalog{ HostCatalog: &store.HostCatalog{ Description: "test-description-repo", ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, }, - wantSecret: []byte(`{"k1":"v1","k2":2,"k3":null}`), + wantSecret: func() *structpb.Struct { + st, err := structpb.NewStruct(map[string]interface{}{ + "k1": "v1", + "k2": 2, + "k3": nil, + }) + require.NoError(t, err) + return st + }(), }, } @@ -170,7 +247,7 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kmsCache := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kmsCache) + repo, err := NewRepository(rw, rw, kmsCache, plgm) assert.NoError(err) assert.NotNil(repo) got, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) @@ -188,6 +265,12 @@ func TestRepository_CreateCatalog(t *testing.T) { assert.Equal(tt.want.Description, got.Description) assert.Equal(got.CreateTime, got.UpdateTime) + // wantedPluginAttributes := &structpb.Struct{} + // require.NoError(t, proto.Unmarshal(tt.want.GetAttributes(), wantedPluginAttributes)) + gotB, err := proto.Marshal(gotPluginAttrs) + require.NoError(t, err) + assert.Equal(tt.want.GetAttributes(), gotB) + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) cSecret := allocHostCatalogSecret() @@ -205,14 +288,17 @@ func TestRepository_CreateCatalog(t *testing.T) { dbWrapper, err := kmsCache.GetWrapper(ctx, got.GetScopeId(), kms.KeyPurposeDatabase) require.NoError(t, err) require.NoError(t, cSecret.decrypt(ctx, dbWrapper)) - assert.Equal(string(tt.wantSecret), string(cSecret.Secret)) + + st := &structpb.Struct{} + require.NoError(t, proto.Unmarshal(cSecret.Secret, st)) + assert.Empty(cmp.Diff(tt.wantSecret, st, protocmp.Transform())) }) } t.Run("invalid-duplicate-names", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) assert.NotNil(repo) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -221,7 +307,7 @@ func TestRepository_CreateCatalog(t *testing.T) { ScopeId: prj.GetPublicId(), Name: "test-name-repo", PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, } @@ -242,7 +328,7 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) assert.NotNil(repo) org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -250,7 +336,7 @@ func TestRepository_CreateCatalog(t *testing.T) { HostCatalog: &store.HostCatalog{ Name: "test-name-repo", PluginId: plg.GetPublicId(), - Attributes: []byte("{}"), + Attributes: []byte{}, }, } in2 := in.clone() @@ -292,6 +378,9 @@ func TestRepository_LookupCatalog(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test", "test") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + } cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) badId, err := newHostCatalogId(ctx, plg.GetIdPrefix()) assert.NoError(t, err) @@ -326,7 +415,7 @@ func TestRepository_LookupCatalog(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) assert.NotNil(repo) @@ -354,10 +443,13 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { wrapper := db.TestWrapper(t) rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) + plg := hostplg.TestPlugin(t, conn, "test", "test") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + } + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(t, err) assert.NotNil(t, repo) - plg := hostplg.TestPlugin(t, conn, "test", "test") const numPerScope = 10 var projs []string diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index cc00d3085..a189331bb 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -14,6 +14,12 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) // CreateSet inserts s into the repository and returns a new HostSet @@ -45,14 +51,15 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } s = s.clone() - c := allocHostCatalog() - c.PublicId = s.GetCatalogId() - if err := r.reader.LookupByPublicId(ctx, c); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host catalog")) + c, err := r.getCatalog(ctx, s.CatalogId) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog")) + } + per, err := r.getPersistedDataForCatalog(ctx, c) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up persisted data")) } - // TODO: Contain the following plugin logic inside a plugin manager as well as - // calling the plugin which we have looked up. plg := hostplg.NewPlugin("", "") plg.PublicId = c.GetPluginId() if err := r.reader.LookupByPublicId(ctx, plg); err != nil { @@ -65,6 +72,24 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } s.PublicId = id + plgClient, ok := r.plugins[plg.GetPublicId()] + if !ok || plgClient == nil { + return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin with plugin name %q not available", plg.GetPluginName())) + } + plgHc, err := toPluginCatalog(ctx, c) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + plgHs, err := toPluginSet(ctx, s) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + if _, err := plgClient.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{Catalog: plgHc, Set: plgHs, Persisted: per}); err != nil { + if status.Code(err) != codes.Unimplemented { + return nil, errors.Wrap(ctx, err, op) + } + } + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) @@ -294,3 +319,22 @@ type hostSetAgg struct { // TableName returns the table name for gorm func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" } + +// toPluginSet returns a host set in the format expected by the host plugin system. +func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { + const op = "plugin.toPluginCatalog" + if in == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin") + } + hs := &pb.HostSet{ + Id: in.GetPublicId(), + } + if in.GetAttributes() != nil { + attrs := &structpb.Struct{} + if err := proto.Unmarshal(in.GetAttributes(), attrs); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to marshal attributes")) + } + hs.Attributes = attrs + } + return hs, nil +} diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index b09655aab..c32c52853 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -16,9 +16,12 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" ) func TestRepository_CreateSet(t *testing.T) { @@ -29,8 +32,20 @@ func TestRepository_CreateSet(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "create", "create") + unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented") + + var pluginReceivedAttrs *structpb.Struct + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + pluginReceivedAttrs = req.GetSet().GetAttributes() + return &plgpb.OnCreateSetResponse{}, nil + }}, + unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + } + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - attrs := []byte("{}") + unimplementedPluginCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + attrs := []byte{} tests := []struct { name string @@ -126,6 +141,23 @@ func TestRepository_CreateSet(t *testing.T) { }, }, }, + { + name: "valid-unimplemented-plugin", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: unimplementedPluginCatalog.PublicId, + Name: "valid-unimplemented-plugin", + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: unimplementedPluginCatalog.PublicId, + Name: "valid-unimplemented-plugin", + Attributes: attrs, + }, + }, + }, { name: "valid-with-description", in: &HostSet{ @@ -143,13 +175,38 @@ func TestRepository_CreateSet(t *testing.T) { }, }, }, + { + name: "valid-with-custom-attributes", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Description: ("test-description-repo"), + Attributes: func() []byte { + b, err := proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"k1": structpb.NewStringValue("foo")}}) + require.NoError(t, err) + return b + }(), + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + Description: ("test-description-repo"), + Attributes: func() []byte { + b, err := proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"k1": structpb.NewStringValue("foo")}}) + require.NoError(t, err) + return b + }(), + }, + }, + }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) require.NoError(err) require.NotNil(repo) got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) @@ -166,13 +223,17 @@ func TestRepository_CreateSet(t *testing.T) { assert.Equal(tt.want.Name, got.GetName()) assert.Equal(tt.want.Description, got.GetDescription()) assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) + wantedPluginAttributes := &structpb.Struct{} + require.NoError(proto.Unmarshal(tt.want.Attributes, wantedPluginAttributes)) + assert.Empty(cmp.Diff(wantedPluginAttributes, pluginReceivedAttrs, protocmp.Transform())) + assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) }) } t.Run("invalid-duplicate-names", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) require.NoError(err) require.NotNil(repo) @@ -183,7 +244,7 @@ func TestRepository_CreateSet(t *testing.T) { HostSet: &store.HostSet{ CatalogId: catalog.PublicId, Name: "test-name-repo", - Attributes: []byte("{}"), + Attributes: []byte{}, }, } @@ -203,7 +264,7 @@ func TestRepository_CreateSet(t *testing.T) { t.Run("valid-duplicate-names-diff-catalogs", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) require.NoError(err) require.NotNil(repo) @@ -214,7 +275,7 @@ func TestRepository_CreateSet(t *testing.T) { in := &HostSet{ HostSet: &store.HostSet{ Name: "test-name-repo", - Attributes: []byte("{}"), + Attributes: []byte{}, }, } in2 := in.clone() @@ -250,9 +311,14 @@ func TestRepository_LookupSet(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "lookup", "lookup") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet := TestSet(t, conn, kms, catalog) + hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + }) hostSetId, err := newHostSetId(ctx, plg.GetIdPrefix()) require.NoError(t, err) @@ -281,7 +347,7 @@ func TestRepository_LookupSet(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) got, err := repo.LookupSet(ctx, tt.in) @@ -307,13 +373,16 @@ func TestRepository_ListSets(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "list", "list") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + } catalogA := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSets := []*HostSet{ - TestSet(t, conn, kms, catalogA), - TestSet(t, conn, kms, catalogA), - TestSet(t, conn, kms, catalogA), + TestSet(t, conn, kms, catalogA, plgm), + TestSet(t, conn, kms, catalogA, plgm), + TestSet(t, conn, kms, catalogA, plgm), } tests := []struct { @@ -343,7 +412,7 @@ func TestRepository_ListSets(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms) + repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) got, err := repo.ListSets(context.Background(), tt.in, tt.opts...) @@ -371,11 +440,14 @@ func TestRepository_ListSets_Limits(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{}, + } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) count := 10 var hostSets []*HostSet for i := 0; i < count; i++ { - hostSets = append(hostSets, TestSet(t, conn, kms, catalog)) + hostSets = append(hostSets, TestSet(t, conn, kms, catalog, plgm)) } tests := []struct { @@ -426,7 +498,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, tt.repoOpts...) + repo, err := NewRepository(rw, rw, kms, plgm, tt.repoOpts...) assert.NoError(err) require.NotNil(repo) got, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...) diff --git a/internal/host/plugin/repository_test.go b/internal/host/plugin/repository_test.go index 51d03d338..22140d143 100644 --- a/internal/host/plugin/repository_test.go +++ b/internal/host/plugin/repository_test.go @@ -3,6 +3,7 @@ package plugin import ( "testing" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,11 +19,14 @@ func TestRepository_New(t *testing.T) { wrapper := db.TestWrapper(t) kmsCache := kms.TestKms(t, conn, wrapper) + plgs := map[string]plgpb.HostPluginServiceServer{} + type args struct { - r db.Reader - w db.Writer - kms *kms.Kms - opts []host.Option + r db.Reader + w db.Writer + kms *kms.Kms + plugins map[string]plgpb.HostPluginServiceServer + opts []host.Option } tests := []struct { @@ -34,38 +38,43 @@ func TestRepository_New(t *testing.T) { { name: "valid", args: args{ - r: rw, - w: rw, - kms: kmsCache, + r: rw, + w: rw, + kms: kmsCache, + plugins: plgs, }, want: &Repository{ reader: rw, writer: rw, kms: kmsCache, + plugins: plgs, defaultLimit: db.DefaultLimit, }, }, { name: "valid-with-limit", args: args{ - r: rw, - w: rw, - kms: kmsCache, - opts: []host.Option{host.WithLimit(5)}, + r: rw, + w: rw, + kms: kmsCache, + plugins: plgs, + opts: []host.Option{host.WithLimit(5)}, }, want: &Repository{ reader: rw, writer: rw, kms: kmsCache, + plugins: plgs, defaultLimit: 5, }, }, { name: "nil-reader", args: args{ - r: nil, - w: rw, - kms: kmsCache, + r: nil, + w: rw, + kms: kmsCache, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -73,9 +82,10 @@ func TestRepository_New(t *testing.T) { { name: "nil-writer", args: args{ - r: rw, - w: nil, - kms: kmsCache, + r: rw, + w: nil, + kms: kmsCache, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -83,9 +93,21 @@ func TestRepository_New(t *testing.T) { { name: "nil-kms", args: args{ - r: rw, - w: rw, - kms: nil, + r: rw, + w: rw, + kms: nil, + plugins: plgs, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-plugins", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + plugins: nil, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -93,9 +115,10 @@ func TestRepository_New(t *testing.T) { { name: "all-nils", args: args{ - r: nil, - w: nil, - kms: nil, + r: nil, + w: nil, + kms: nil, + plugins: nil, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -105,7 +128,7 @@ func TestRepository_New(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...) + got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.plugins, tt.args.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 17a7cd29c..2b88558b7 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.8 +// protoc v3.17.3 // source: controller/storage/host/plugin/store/v1/host.proto // Package store provides protobufs for storing types in the plugin host @@ -56,8 +56,8 @@ type HostCatalog struct { // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` // attributes is a jsonb formatted field. - // @inject_tag: `gorm:"default:null"` - Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"not_null"` + Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"` } func (x *HostCatalog) Reset() { @@ -184,8 +184,8 @@ type HostSet struct { // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` // attributes is a byte field containing marshaled JSON data - // @inject_tag: `gorm:"default:null"` - Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"not_null"` + Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"` // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index 1a860399d..cc149478d 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,13 +41,13 @@ func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...O // TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, opt ...Option) *HostSet { +func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceServer, opt ...Option) *HostSet { t.Helper() require := require.New(t) ctx := context.Background() rw := db.New(conn) - repo, err := NewRepository(rw, rw, kmsCache) + repo, err := NewRepository(rw, rw, kmsCache, plgm) require.NoError(err) set, err := NewHostSet(ctx, hc.PublicId, opt...) @@ -66,3 +67,64 @@ func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, op return set } + +// TestPluginServer provides a host plugin service server where each method can be overwritten for tests. +type TestPluginServer struct { + OnCreateCatalogFn func(context.Context, *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) + OnUpdateCatalogFn func(context.Context, *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) + OnDeleteCatalogFn func(context.Context, *plgpb.OnDeleteCatalogRequest) (*plgpb.OnDeleteCatalogResponse, error) + OnCreateSetFn func(context.Context, *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) + OnUpdateSetFn func(context.Context, *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) + OnDeleteSetFn func(context.Context, *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) + ListHostsFn func(context.Context, *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) + plgpb.UnimplementedHostPluginServiceServer +} + +func (t TestPluginServer) OnCreateCatalog(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + if t.OnCreateCatalogFn == nil { + return t.UnimplementedHostPluginServiceServer.OnCreateCatalog(ctx, req) + } + return t.OnCreateCatalogFn(ctx, req) +} + +func (t TestPluginServer) OnUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) { + if t.OnUpdateCatalogFn == nil { + return t.UnimplementedHostPluginServiceServer.OnUpdateCatalog(ctx, req) + } + return t.OnUpdateCatalogFn(ctx, req) +} + +func (t TestPluginServer) OnDeleteCatalog(ctx context.Context, req *plgpb.OnDeleteCatalogRequest) (*plgpb.OnDeleteCatalogResponse, error) { + if t.OnDeleteCatalogFn == nil { + return t.UnimplementedHostPluginServiceServer.OnDeleteCatalog(ctx, req) + } + return t.OnDeleteCatalogFn(ctx, req) +} + +func (t TestPluginServer) OnCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + if t.OnCreateSetFn == nil { + return t.UnimplementedHostPluginServiceServer.OnCreateSet(ctx, req) + } + return t.OnCreateSetFn(ctx, req) +} + +func (t TestPluginServer) OnUpdateSet(ctx context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) { + if t.OnUpdateSetFn == nil { + return t.UnimplementedHostPluginServiceServer.OnUpdateSet(ctx, req) + } + return t.OnUpdateSetFn(ctx, req) +} + +func (t TestPluginServer) OnDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) { + if t.OnDeleteSetFn == nil { + return t.UnimplementedHostPluginServiceServer.OnDeleteSet(ctx, req) + } + return t.OnDeleteSetFn(ctx, req) +} + +func (t TestPluginServer) ListHosts(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + if t.ListHostsFn == nil { + return t.UnimplementedHostPluginServiceServer.ListHosts(ctx, req) + } + return t.ListHostsFn(ctx, req) +} diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index 50419aef7..d5b7efbf1 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -46,7 +47,7 @@ func Test_TestSet(t *testing.T) { assert.NotEmpty(plg.GetPublicId()) c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) - set := TestSet(t, conn, kmsCache, c, WithName("foo"), WithDescription("bar")) + set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceServer{plg.GetPublicId(): &TestPluginServer{}}, WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix"))) assert.Equal("foo", set.GetName()) diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go index a74310f9d..aaaca17e7 100644 --- a/internal/host/preferred_endpoint_test.go +++ b/internal/host/preferred_endpoint_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +27,9 @@ func TestPreferredEndpoint_Create(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create", "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog) + set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) type args struct { hostSetId string @@ -154,7 +157,9 @@ func TestPreferredEndpoint_Delete(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create", "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog) + set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + }) peFunc := func(priority uint32, condition string) *host.PreferredEndpoint { ep, err := host.NewPreferredEndpoint(ctx, set.PublicId, priority, condition) diff --git a/internal/plugin/host/store/plugin.pb.go b/internal/plugin/host/store/plugin.pb.go index 05c63df53..ac2cade04 100644 --- a/internal/plugin/host/store/plugin.pb.go +++ b/internal/plugin/host/store/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.8 +// protoc v3.17.3 // source: controller/storage/plugin/host/store/v1/plugin.proto // Package store provides protobufs for storing types in the host package. diff --git a/internal/plugin/store/plugin.pb.go b/internal/plugin/store/plugin.pb.go index 54d39c526..2514d8a1c 100644 --- a/internal/plugin/store/plugin.pb.go +++ b/internal/plugin/store/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.8 +// protoc v3.17.3 // source: controller/storage/plugin/store/v1/plugin.proto // Package store provides protobufs for storing types in the host package. diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index d5c8b9626..37f1c2141 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -43,7 +43,7 @@ message HostCatalog { uint32 version = 8; // attributes is a jsonb formatted field. - // @inject_tag: `gorm:"default:null"` + // @inject_tag: `gorm:"not_null"` bytes attributes = 9; } @@ -79,7 +79,7 @@ message HostSet { uint32 version = 7; // attributes is a byte field containing marshaled JSON data - // @inject_tag: `gorm:"default:null"` + // @inject_tag: `gorm:"not_null"` bytes attributes = 8; // preferred_endpoints stores string preference values diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 850216571..eca57cf3e 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/patrickmn/go-cache" @@ -136,7 +137,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { return static.NewRepository(dbase, dbase, c.kms) } c.PluginHostRepoFn = func() (*hostplugin.Repository, error) { - return hostplugin.NewRepository(dbase, dbase, c.kms) + return hostplugin.NewRepository(dbase, dbase, c.kms, map[string]plgpb.HostPluginServiceServer{}) } c.HostPluginRepoFn = func() (*host.Repository, error) { return host.NewRepository(dbase, dbase, c.kms) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 6479c12eb..5772131d1 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -2,7 +2,6 @@ package host_catalogs import ( "context" - "encoding/json" "fmt" "strings" @@ -26,6 +25,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -603,16 +603,13 @@ func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb. if outputFields.Has(globals.AttributesField) { switch h := in.(type) { case *plugin.HostCatalog: - attrs := map[string]interface{}{} - err := json.Unmarshal(h.Attributes, &attrs) + attrs := &structpb.Struct{} + err := proto.Unmarshal(h.Attributes, attrs) if err != nil { return nil, errors.Wrap(ctx, err, op) } - if len(attrs) > 0 { - out.Attributes, err = structpb.NewStruct(attrs) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } + if len(attrs.GetFields()) > 0 { + out.Attributes = attrs } } } @@ -645,10 +642,10 @@ func toStoragePluginCatalog(ctx context.Context, scopeId, plgId string, item *pb opts = append(opts, plugin.WithDescription(desc.GetValue())) } if attrs := item.GetAttributes(); attrs != nil { - opts = append(opts, plugin.WithAttributes(attrs.AsMap())) + opts = append(opts, plugin.WithAttributes(attrs)) } if secrets := item.GetSecrets(); secrets != nil { - opts = append(opts, plugin.WithSecrets(secrets.AsMap())) + opts = append(opts, plugin.WithSecrets(secrets)) } hc, err := plugin.NewHostCatalog(ctx, scopeId, plgId, opts...) if err != nil { diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index e3a684b1f..6f2c64799 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc/codes" "google.golang.org/protobuf/proto" @@ -62,7 +63,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -151,7 +152,7 @@ func TestGet_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -240,7 +241,7 @@ func TestList(t *testing.T) { return iam.TestRepo(t, conn, wrapper), nil } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -433,7 +434,7 @@ func TestDelete(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -503,7 +504,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -538,7 +539,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -688,9 +689,6 @@ func TestCreate_Plugin(t *testing.T) { repo := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) - } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) } @@ -700,6 +698,15 @@ func TestCreate_Plugin(t *testing.T) { name := "test" plg := host.TestPlugin(t, conn, name, name) + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{ + OnCreateCatalogFn: func(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + return nil, nil + }, + }, + }) + } defaultHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) defaultHcCreated := defaultHc.GetCreateTime().GetTimestamp().AsTime() toMerge := &pbs.CreateHostCatalogRequest{} @@ -845,7 +852,7 @@ func TestUpdate(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index f4498fa65..f4d8323f1 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -2,7 +2,6 @@ package host_sets import ( "context" - "encoding/json" "fmt" "strings" @@ -24,6 +23,7 @@ import ( pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/go-secure-stdlib/strutil" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -756,16 +756,13 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler out.PreferredEndpoints = h.GetPreferredEndpoints() } if outputFields.Has(globals.AttributesField) { - attrs := map[string]interface{}{} - err := json.Unmarshal(h.Attributes, &attrs) + attrs := &structpb.Struct{} + err := proto.Unmarshal(h.Attributes, attrs) if err != nil { return nil, errors.Wrap(ctx, err, op) } - if len(attrs) > 0 { - out.Attributes, err = structpb.NewStruct(attrs) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } + if len(attrs.GetFields()) > 0 { + out.Attributes = attrs } } } @@ -799,7 +796,7 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) opts = append(opts, plugin.WithDescription(item.GetDescription().GetValue())) } if item.GetAttributes() != nil { - opts = append(opts, plugin.WithAttributes(item.GetAttributes().AsMap())) + opts = append(opts, plugin.WithAttributes(item.GetAttributes())) } if item.GetPreferredEndpoints() != nil { opts = append(opts, plugin.WithPreferredEndpoints(item.GetPreferredEndpoints())) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 7722b5efe..09ab45673 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/genproto/protobuf/field_mask" @@ -54,7 +55,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -147,14 +148,16 @@ func TestGet_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } name := "test" prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} plg := hostplugin.TestPlugin(t, conn, name, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(prefEndpoints)) + hs := plugin.TestSet(t, conn, kms, hc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }, plugin.WithPreferredEndpoints(prefEndpoints)) toMerge := &pbs.GetHostSetRequest{} @@ -241,7 +244,7 @@ func TestList_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2) hc, hcNoHosts := hcs[0], hcs[1] @@ -338,18 +341,21 @@ func TestList_Plugin(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) - } name := "test" plg := hostplugin.TestPlugin(t, conn, name, name) + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, plgm) + } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) preferredEndpoints := []string{"cidr:1.2.3.4", "dns:*.foobar.com"} var wantHs []*pb.HostSet for i := 0; i < 10; i++ { - h := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(preferredEndpoints)) + h := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) wantHs = append(wantHs, &pb.HostSet{ Id: h.GetPublicId(), HostCatalogId: h.GetCatalogId(), @@ -443,7 +449,7 @@ func TestDelete(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -522,7 +528,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -558,7 +564,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -700,11 +706,17 @@ func TestCreate_Plugin(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) - } name := "test" plg := hostplugin.TestPlugin(t, conn, name, name) + plgRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{ + OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + return nil, nil + }, + }, + }) + } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) testAttrs, err := structpb.NewStruct(map[string]interface{}{ @@ -956,7 +968,7 @@ func TestUpdate(t *testing.T) { } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } tested, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Failed to create a new host set service.") @@ -1295,7 +1307,7 @@ func TestAddHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1415,7 +1427,7 @@ func TestSetHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1531,7 +1543,7 @@ func TestRemoveHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index ac7d36e02..98da7aab1 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -38,6 +38,7 @@ import ( credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" @@ -983,7 +984,9 @@ func TestAddTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) addCases := []struct { name string @@ -1115,7 +1118,9 @@ func TestSetTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) setCases := []struct { name string @@ -1241,7 +1246,9 @@ func TestRemoveTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) removeCases := []struct { name string @@ -1379,7 +1386,9 @@ func TestAddTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) addCases := []struct { name string @@ -1511,7 +1520,9 @@ func TestSetTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) setCases := []struct { name string @@ -1631,7 +1642,9 @@ func TestRemoveTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test", "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc) + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &plugin.TestPluginServer{}, + }) removeCases := []struct { name string diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index e90bad60e..a8719f737 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.8 +// protoc v3.17.3 // source: plugin/v1/host_plugin_service.proto package plugin From 8fb1898c3ebb55b6a2233f1004f5cdc7f4de152a Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 22 Sep 2021 14:37:13 -0400 Subject: [PATCH 19/71] Update host plugin RPC to return sets from ListHosts (#1557) A few other minor tweaks as well. --- .../host/plugin/repository_host_catalog.go | 10 +- .../plugin/repository_host_catalog_test.go | 2 +- .../local/plugin/v1/host_plugin_service.proto | 26 ++- sdk/pbs/plugin/host_plugin_service.pb.go | 213 ++++++++++-------- 4 files changed, 139 insertions(+), 112 deletions(-) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index aac135385..0fc165c42 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -81,8 +81,8 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } var hcSecret *HostCatalogSecret - if plgResp != nil && plgResp.GetPersisted().GetData() != nil { - hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetData()) + if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { + hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetSecrets()) if err != nil { return nil, errors.Wrap(ctx, err, op) } @@ -229,11 +229,11 @@ func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCata return nil, errors.Wrap(ctx, err, op) } - data := &structpb.Struct{} - if err := proto.Unmarshal(cSecret.GetSecret(), data); err != nil { + secrets := &structpb.Struct{} + if err := proto.Unmarshal(cSecret.GetSecret(), secrets); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unmarshaling secret")) } - return &plgpb.HostCatalogPersisted{Data: data}, nil + return &plgpb.HostCatalogPersisted{Secrets: secrets}, nil } // toPluginCatalog returns a host catalog, with it's secret if available, in the format expected diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 9a25af2b7..e3f8ed94a 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -38,7 +38,7 @@ func TestRepository_CreateCatalog(t *testing.T) { plg.GetPublicId(): &TestPluginServer{ OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { gotPluginAttrs = req.GetCatalog().GetAttributes() - return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Data: req.GetCatalog().GetSecrets()}}, nil + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil }, }, unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, diff --git a/internal/proto/local/plugin/v1/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto index bef0ebbf4..4de9aaf58 100644 --- a/internal/proto/local/plugin/v1/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -117,7 +117,7 @@ message OnDeleteSetRequest { controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; // The host set to delete. - controller.api.resources.hostsets.v1.HostSet current_set = 20; + controller.api.resources.hostsets.v1.HostSet set = 20; // The persisted data for the host catalog that the set belongs to. HostCatalogPersisted persisted = 30; @@ -152,22 +152,28 @@ message ListHostsResponseHost { // Required. Any and all known IP addresses for the host. repeated string ip_addresses = 20; + // Required. Any and all known DNS names for the host. + repeated string dns_names = 30; + + // Required. The host set IDs that match this host, out of the host sets + // sent in the request. + repeated string set_ids = 40; + // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network // addresses, etc. google.protobuf.Struct attributes = 100; } -// HostCatalogPersisted represents state persisted between host -// catalog calls. Its intended purpose is to store authentication data -// required by the plugin to make calls to its respective cloud API. +// HostCatalogPersisted represents state persisted between host catalog calls. +// Its intended purpose is to store authentication data required by the plugin +// to make calls to its respective cloud API. // -// The data stored in this message is encrypted at-rest by Boundary -// and never returned to the end user. +// The secrets stored in this message are encrypted at-rest by Boundary and +// never returned to the end user. // -// TODO: Add a size limit to this data before we export the plugin -// SDK. +// TODO: Add a size limit to this data before we export the plugin SDK. message HostCatalogPersisted { - // The persisted data. - google.protobuf.Struct data = 100; + // The persisted secrets. + google.protobuf.Struct secrets = 100; } diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index a8719f737..459450ee9 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -564,7 +564,7 @@ type OnDeleteSetRequest struct { // The host catalog that the set belongs to. Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` // The host set to delete. - CurrentSet *hostsets.HostSet `protobuf:"bytes,20,opt,name=current_set,json=currentSet,proto3" json:"current_set,omitempty"` + Set *hostsets.HostSet `protobuf:"bytes,20,opt,name=set,proto3" json:"set,omitempty"` // The persisted data for the host catalog that the set belongs to. Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` } @@ -608,9 +608,9 @@ func (x *OnDeleteSetRequest) GetCatalog() *hostcatalogs.HostCatalog { return nil } -func (x *OnDeleteSetRequest) GetCurrentSet() *hostsets.HostSet { +func (x *OnDeleteSetRequest) GetSet() *hostsets.HostSet { if x != nil { - return x.CurrentSet + return x.Set } return nil } @@ -787,6 +787,11 @@ type ListHostsResponseHost struct { ExternalId string `protobuf:"bytes,10,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` // Required. Any and all known IP addresses for the host. IpAddresses []string `protobuf:"bytes,20,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` + // Required. Any and all known DNS names for the host. + DnsNames []string `protobuf:"bytes,30,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` + // Required. The host set IDs that match this host, out of the host sets + // sent in the request. + SetIds []string `protobuf:"bytes,40,rep,name=set_ids,json=setIds,proto3" json:"set_ids,omitempty"` // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network // addresses, etc. @@ -839,6 +844,20 @@ func (x *ListHostsResponseHost) GetIpAddresses() []string { return nil } +func (x *ListHostsResponseHost) GetDnsNames() []string { + if x != nil { + return x.DnsNames + } + return nil +} + +func (x *ListHostsResponseHost) GetSetIds() []string { + if x != nil { + return x.SetIds + } + return nil +} + func (x *ListHostsResponseHost) GetAttributes() *structpb.Struct { if x != nil { return x.Attributes @@ -846,22 +865,21 @@ func (x *ListHostsResponseHost) GetAttributes() *structpb.Struct { return nil } -// HostCatalogPersisted represents state persisted between host -// catalog calls. Its intended purpose is to store authentication data -// required by the plugin to make calls to its respective cloud API. +// HostCatalogPersisted represents state persisted between host catalog calls. +// Its intended purpose is to store authentication data required by the plugin +// to make calls to its respective cloud API. // -// The data stored in this message is encrypted at-rest by Boundary -// and never returned to the end user. +// The secrets stored in this message are encrypted at-rest by Boundary and +// never returned to the end user. // -// TODO: Add a size limit to this data before we export the plugin -// SDK. +// TODO: Add a size limit to this data before we export the plugin SDK. type HostCatalogPersisted struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The persisted data. - Data *structpb.Struct `protobuf:"bytes,100,opt,name=data,proto3" json:"data,omitempty"` + // The persisted secrets. + Secrets *structpb.Struct `protobuf:"bytes,100,opt,name=secrets,proto3" json:"secrets,omitempty"` } func (x *HostCatalogPersisted) Reset() { @@ -896,9 +914,9 @@ func (*HostCatalogPersisted) Descriptor() ([]byte, []int) { return file_plugin_v1_host_plugin_service_proto_rawDescGZIP(), []int{15} } -func (x *HostCatalogPersisted) GetData() *structpb.Struct { +func (x *HostCatalogPersisted) GetSecrets() *structpb.Struct { if x != nil { - return x.Data + return x.Secrets } return nil } @@ -1005,98 +1023,101 @@ var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf4, 0x01, 0x0a, 0x12, 0x4f, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, - 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, - 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, - 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, + 0x6f, 0x67, 0x12, 0x3f, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, + 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, + 0x73, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, + 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x4c, 0x69, + 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, + 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, + 0x41, 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x04, 0x73, 0x65, + 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, - 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, - 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x41, - 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x04, 0x73, 0x65, 0x74, - 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, - 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, - 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x94, 0x01, - 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 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, 0x64, 0x61, 0x74, 0x61, 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, - 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x64, 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0xca, + 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x1e, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x65, 0x74, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x74, 0x49, + 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x64, 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, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x14, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x64, + 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, 0x07, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, + 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, + 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, - 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, - 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, + 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, + 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1150,14 +1171,14 @@ var file_plugin_v1_host_plugin_service_proto_depIdxs = []int32{ 17, // 13: plugin.v1.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet 15, // 14: plugin.v1.OnUpdateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted 16, // 15: plugin.v1.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 16: plugin.v1.OnDeleteSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 17, // 16: plugin.v1.OnDeleteSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet 15, // 17: plugin.v1.OnDeleteSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted 16, // 18: plugin.v1.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog 17, // 19: plugin.v1.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet 15, // 20: plugin.v1.ListHostsRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted 14, // 21: plugin.v1.ListHostsResponse.hosts:type_name -> plugin.v1.ListHostsResponseHost 18, // 22: plugin.v1.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct - 18, // 23: plugin.v1.HostCatalogPersisted.data:type_name -> google.protobuf.Struct + 18, // 23: plugin.v1.HostCatalogPersisted.secrets:type_name -> google.protobuf.Struct 0, // 24: plugin.v1.HostPluginService.OnCreateCatalog:input_type -> plugin.v1.OnCreateCatalogRequest 2, // 25: plugin.v1.HostPluginService.OnUpdateCatalog:input_type -> plugin.v1.OnUpdateCatalogRequest 4, // 26: plugin.v1.HostPluginService.OnDeleteCatalog:input_type -> plugin.v1.OnDeleteCatalogRequest From e4e788553f3019e9580618b62c058c21d681dc3a Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 23 Sep 2021 08:33:54 -0400 Subject: [PATCH 20/71] Add basic loopback test plugin (#1555) --- internal/host/plugin/loopback.go | 116 +++++++++++++++ internal/host/plugin/loopback_test.go | 199 ++++++++++++++++++++++++++ internal/host/plugin/testing.go | 2 + 3 files changed, 317 insertions(+) create mode 100644 internal/host/plugin/loopback.go create mode 100644 internal/host/plugin/loopback_test.go diff --git a/internal/host/plugin/loopback.go b/internal/host/plugin/loopback.go new file mode 100644 index 000000000..7202493a4 --- /dev/null +++ b/internal/host/plugin/loopback.go @@ -0,0 +1,116 @@ +package plugin + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/mitchellh/mapstructure" +) + +const loopbackPluginHostInfoAttrField = "host_info" + +var _ plgpb.HostPluginServiceServer = (*loopbackPlugin)(nil) + +// loopbackPlugin provides a host plugin with functionality useful for certain +// kinds of testing. It is not (currently) thread-safe. +// +// Over time, if useful, it can be enhanced to do things like handle multiple +// hosts per set. +type loopbackPlugin struct { + *TestPluginServer + + hostMap map[string]*loopbackPluginHostInfo +} + +type loopbackPluginHostInfo struct { + ExternalId string `mapstructure:"external_id"` + IpAddresses []string `mapstructure:"ip_addresses"` + DnsNames []string `mapstructure:"dns_names"` +} + +// NewLoopbackPlugin returns a new loopback plugin +func NewLoopbackPlugin() plgpb.HostPluginServiceServer { + ret := &loopbackPlugin{ + TestPluginServer: new(TestPluginServer), + hostMap: make(map[string]*loopbackPluginHostInfo), + } + ret.OnCreateCatalogFn = ret.onCreateCatalog + ret.OnCreateSetFn = ret.onCreateSet + ret.OnDeleteSetFn = ret.onDeleteSet + ret.ListHostsFn = ret.listHosts + return ret +} + +func (l *loopbackPlugin) onCreateCatalog(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + const op = "plugin.(loopbackPlugin).onCreateCatalog" + if req == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil") + } + if cat := req.GetCatalog(); cat != nil { + if secrets := cat.GetSecrets(); secrets != nil { + return &plgpb.OnCreateCatalogResponse{ + Persisted: &plgpb.HostCatalogPersisted{ + Secrets: secrets, + }, + }, nil + } + } + return nil, nil +} + +func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + const op = "plugin.(loopbackPlugin).onCreateSet" + if req == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil") + } + set := req.GetSet() + if set == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil") + } + if attrs := set.GetAttributes(); attrs != nil { + attrsMap := attrs.AsMap() + if field := attrsMap[loopbackPluginHostInfoAttrField]; field != nil { + hostInfo := new(loopbackPluginHostInfo) + if err := mapstructure.Decode(field, hostInfo); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + l.hostMap[set.GetId()] = hostInfo + } + } + return nil, nil +} + +func (l *loopbackPlugin) onDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) { + const op = "plugin.(loopbackPlugin).onDeleteSet" + if req == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil") + } + set := req.GetSet() + if set == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil") + } + delete(l.hostMap, set.GetId()) + return nil, nil +} + +func (l *loopbackPlugin) listHosts(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + const op = "plugin.(loopbackPlugin).listHosts" + if req == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil") + } + resp := new(plgpb.ListHostsResponse) + for _, set := range req.GetSets() { + hostInfo := l.hostMap[set.GetId()] + if hostInfo == nil { + continue + } + resp.Hosts = append(resp.Hosts, &plgpb.ListHostsResponseHost{ + SetIds: []string{set.GetId()}, + ExternalId: hostInfo.ExternalId, + IpAddresses: hostInfo.IpAddresses, + DnsNames: hostInfo.DnsNames, + }) + } + return resp, nil +} diff --git a/internal/host/plugin/loopback_test.go b/internal/host/plugin/loopback_test.go new file mode 100644 index 000000000..ec73cd731 --- /dev/null +++ b/internal/host/plugin/loopback_test.go @@ -0,0 +1,199 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + ta "github.com/stretchr/testify/assert" + tr "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" +) + +// TestLoopbackPlugin is a quick test of basic functionality. +func TestLoopbackPlugin(t *testing.T) { + require, assert := tr.New(t), ta.New(t) + ctx := context.Background() + + plg := NewLoopbackPlugin() + secretsMap := map[string]interface{}{ + "foo": "bar", + "baz": true, + } + secrets, err := structpb.NewStruct(secretsMap) + require.NoError(err) + + // First, test that if we give it secrets, those secrets come back as + // persisted data + catResp, err := plg.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{ + Catalog: &hostcatalogs.HostCatalog{ + Secrets: secrets, + }, + }) + require.NoError(err) + require.NotNil(catResp) + require.NotNil(catResp.GetPersisted()) + require.NotNil(catResp.GetPersisted().GetSecrets()) + assert.EqualValues(secretsMap, catResp.GetPersisted().GetSecrets().AsMap()) + + // Add data to some sets + hostInfo1 := map[string]interface{}{ + loopbackPluginHostInfoAttrField: map[string]interface{}{ + "set_ids": []interface{}{"set1"}, + "external_id": "host1", + "ip_addresses": []interface{}{"1.2.3.4", "2.3.4.5"}, + "dns_names": []interface{}{"foo.com"}, + }, + } + attrs, err := structpb.NewStruct(hostInfo1) + require.NoError(err) + _, err = plg.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{ + Set: &hostsets.HostSet{ + Id: "set1", + Attributes: attrs, + }, + }) + require.NoError(err) + hostInfo2 := map[string]interface{}{ + loopbackPluginHostInfoAttrField: map[string]interface{}{ + "set_ids": []interface{}{"set2"}, + "external_id": "host2", + "ip_addresses": []interface{}{"5.6.7.8", "6.7.8.9"}, + "dns_names": []interface{}{"bar.com"}, + }, + } + attrs, err = structpb.NewStruct(hostInfo2) + require.NoError(err) + _, err = plg.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{ + Set: &hostsets.HostSet{ + Id: "set2", + Attributes: attrs, + }, + }) + require.NoError(err) + + // Define test struct and validation function + type testInfo struct { + name string + sets []string + found []map[string]interface{} + } + validateSets := func(t *testing.T, tt testInfo) { + require, assert := tr.New(t), ta.New(t) + var hostSets []*hostsets.HostSet + for _, set := range tt.sets { + hostSets = append(hostSets, &hostsets.HostSet{Id: set}) + } + resp, err := plg.ListHosts(ctx, &plgpb.ListHostsRequest{ + Sets: hostSets, + }) + require.NoError(err) + if len(tt.found) == 0 { + assert.Len(resp.GetHosts(), 0) + return + } + + require.Greater(len(resp.GetHosts()), 0) + + var found []map[string]interface{} + for _, host := range resp.GetHosts() { + hostMap := map[string]interface{}{ + "external_id": host.GetExternalId(), + } + var sets []interface{} + for _, set := range host.SetIds { + sets = append(sets, set) + } + var ips []interface{} + for _, ip := range host.GetIpAddresses() { + ips = append(ips, ip) + } + var names []interface{} + for _, name := range host.GetDnsNames() { + names = append(names, name) + } + if len(sets) > 0 { + hostMap["set_ids"] = sets + } + if len(ips) > 0 { + hostMap["ip_addresses"] = ips + } + if len(names) > 0 { + hostMap["dns_names"] = names + } + found = append(found, hostMap) + } + assert.ElementsMatch(tt.found, found) + } + + // First set of tests: check that we can look up sets individually and + // together + setTests := []testInfo{ + { + name: "set 1", + sets: []string{"set1"}, + found: []map[string]interface{}{ + hostInfo1[loopbackPluginHostInfoAttrField].(map[string]interface{}), + }, + }, + { + name: "set 2", + sets: []string{"set2"}, + found: []map[string]interface{}{ + hostInfo2[loopbackPluginHostInfoAttrField].(map[string]interface{}), + }, + }, + { + name: "sets 1 and 2", + sets: []string{"set1", "set2"}, + found: []map[string]interface{}{ + hostInfo1[loopbackPluginHostInfoAttrField].(map[string]interface{}), + hostInfo2[loopbackPluginHostInfoAttrField].(map[string]interface{}), + }, + }, + } + for _, tt := range setTests { + t.Run(tt.name, func(t *testing.T) { + validateSets(t, tt) + }) + } + + // Remove a set + _, err = plg.OnDeleteSet(ctx, &plgpb.OnDeleteSetRequest{ + Set: &hostsets.HostSet{ + Id: "set1", + }, + }) + require.NoError(err) + + // Run tests again, making sure we no longer find that host either + // individually or together + setTests = []testInfo{ + { + name: "set 1 deleted", + sets: []string{"set1"}, + found: []map[string]interface{}{}, + }, + { + name: "set 2 not deleted", + sets: []string{"set2"}, + found: []map[string]interface{}{ + hostInfo2[loopbackPluginHostInfoAttrField].(map[string]interface{}), + }, + }, + { + name: "sets 1 and 2 set 1 deleted", + sets: []string{"set1", "set2"}, + found: []map[string]interface{}{ + hostInfo2[loopbackPluginHostInfoAttrField].(map[string]interface{}), + }, + }, + } + for _, tt := range setTests { + t.Run(tt.name, func(t *testing.T) { + validateSets(t, tt) + }) + } +} diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index cc149478d..ed4c502df 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -68,6 +68,8 @@ func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, pl return set } +var _ plgpb.HostPluginServiceServer = (*TestPluginServer)(nil) + // TestPluginServer provides a host plugin service server where each method can be overwritten for tests. type TestPluginServer struct { OnCreateCatalogFn func(context.Context, *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) From a4ea99047f08d099261ad203ca9eade976c12b65 Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 24 Sep 2021 13:16:13 -0700 Subject: [PATCH 21/71] Remove Prefix Id and Plugin Name fields (#1563) * Remove Prefix Id and Plugin Name fields. Creating catalogs now require either the plugin id or the name of the plugin resource (but not both). Host Catalogs and Host Sets API resources now have a read only plugin field populated with plugin info. Host Catalogs have a plugin id field as a top level field. --- api/hostcatalogs/host_catalog.gen.go | 3 + api/hosts/host.gen.go | 2 + api/hostsets/host_set.gen.go | 2 + api/plugins/plugin_info.gen.go | 8 + globals/fields.go | 2 + internal/api/genapi/input.go | 6 + .../postgres/17/02_hostplugin.up.sql | 16 +- .../postgres/17/05_preferred_endpoints.up.sql | 6 +- internal/db/schema/postgres_migration.gen.go | 22 +- internal/gen/controller.swagger.json | 46 ++++ .../api/services/host_catalog_service.pb.go | 216 +++++++++--------- .../services/host_catalog_service.pb.gw.go | 18 ++ .../host/plugin/host_catalog_secret_test.go | 4 +- internal/host/plugin/host_catalog_test.go | 22 +- internal/host/plugin/host_set_test.go | 4 +- internal/host/plugin/ids.go | 15 +- internal/host/plugin/immutable_fields_test.go | 6 +- .../host/plugin/repository_host_catalog.go | 98 ++++---- .../plugin/repository_host_catalog_test.go | 27 +-- internal/host/plugin/repository_host_set.go | 104 +++++---- .../host/plugin/repository_host_set_test.go | 44 ++-- internal/host/plugin/testing.go | 10 +- internal/host/plugin/testing_test.go | 10 +- internal/host/preferred_endpoint_test.go | 4 +- internal/perms/output_fields.go | 2 + internal/perms/output_fields_test.go | 2 + internal/plugin/host/ids.go | 2 +- internal/plugin/host/immutable_fields_test.go | 11 +- internal/plugin/host/plugin.go | 4 +- internal/plugin/host/plugin_test.go | 168 ++------------ internal/plugin/host/repository_plugin.go | 22 +- .../plugin/host/repository_plugin_test.go | 92 +------- internal/plugin/host/store/plugin.pb.go | 38 +-- internal/plugin/host/testing.go | 4 +- internal/plugin/host/testing_test.go | 2 +- .../hostcatalogs/v1/host_catalog.proto | 7 + .../api/resources/hosts/v1/host.proto | 4 + .../api/resources/hostsets/v1/host_set.proto | 4 + .../api/resources/plugins/v1/plugin.proto | 16 ++ .../services/v1/host_catalog_service.proto | 3 + .../storage/plugin/host/store/v1/plugin.proto | 8 - .../host_catalogs/host_catalog_service.go | 181 +++++++++------ .../host_catalog_service_test.go | 125 ++++++---- .../handlers/host_sets/host_set_service.go | 182 ++++++++------- .../host_sets/host_set_service_test.go | 97 +++++--- .../servers/controller/handlers/options.go | 10 + .../handlers/targets/target_service_test.go | 12 +- .../resources/hostcatalogs/host_catalog.pb.go | 200 +++++++++------- .../controller/api/resources/hosts/host.pb.go | 156 +++++++------ .../api/resources/hostsets/host_set.pb.go | 147 ++++++------ .../api/resources/plugins/plugin.pb.go | 172 ++++++++++++++ 51 files changed, 1314 insertions(+), 1052 deletions(-) create mode 100644 api/plugins/plugin_info.gen.go create mode 100644 internal/proto/local/controller/api/resources/plugins/v1/plugin.proto create mode 100644 sdk/pbs/controller/api/resources/plugins/plugin.pb.go diff --git a/api/hostcatalogs/host_catalog.gen.go b/api/hostcatalogs/host_catalog.gen.go index abce123ff..ae121ae42 100644 --- a/api/hostcatalogs/host_catalog.gen.go +++ b/api/hostcatalogs/host_catalog.gen.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/plugins" "github.com/hashicorp/boundary/api/scopes" ) @@ -16,6 +17,8 @@ type HostCatalog struct { Id string `json:"id,omitempty"` ScopeId string `json:"scope_id,omitempty"` Scope *scopes.ScopeInfo `json:"scope,omitempty"` + PluginId string `json:"plugin_id,omitempty"` + Plugin *plugins.PluginInfo `json:"plugin,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` CreatedTime time.Time `json:"created_time,omitempty"` diff --git a/api/hosts/host.gen.go b/api/hosts/host.gen.go index 51b6b424a..24dcc2178 100644 --- a/api/hosts/host.gen.go +++ b/api/hosts/host.gen.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/plugins" "github.com/hashicorp/boundary/api/scopes" ) @@ -16,6 +17,7 @@ type Host struct { Id string `json:"id,omitempty"` HostCatalogId string `json:"host_catalog_id,omitempty"` Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Plugin *plugins.PluginInfo `json:"plugin,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` CreatedTime time.Time `json:"created_time,omitempty"` diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index 58fceb29c..36a8b6c27 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/plugins" "github.com/hashicorp/boundary/api/scopes" ) @@ -16,6 +17,7 @@ type HostSet struct { Id string `json:"id,omitempty"` HostCatalogId string `json:"host_catalog_id,omitempty"` Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Plugin *plugins.PluginInfo `json:"plugin,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` CreatedTime time.Time `json:"created_time,omitempty"` diff --git a/api/plugins/plugin_info.gen.go b/api/plugins/plugin_info.gen.go new file mode 100644 index 000000000..2eb390ddd --- /dev/null +++ b/api/plugins/plugin_info.gen.go @@ -0,0 +1,8 @@ +// Code generated by "make api"; DO NOT EDIT. +package plugins + +type PluginInfo struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` +} diff --git a/globals/fields.go b/globals/fields.go index 00f8b898d..0ddaacf44 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -59,4 +59,6 @@ const ( ApplicationCredentialSourceIdsField = "application_credential_source_ids" ApplicationCredentialSourcesField = "application_credential_sources" PreferredEndpointsField = "preferred_endpoints" + PluginIdField = "plugin_id" + PluginField = "plugin" ) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 9715484fc..938699ea1 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/managedgroups" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions" @@ -138,6 +139,11 @@ var inputStructs = []*structInfo{ outFile: "scopes/scope_info.gen.go", skipOptions: true, }, + { + inProto: &plugins.PluginInfo{}, + outFile: "plugins/plugin_info.gen.go", + skipOptions: true, + }, { inProto: &scopes.Scope{}, outFile: "scopes/scope.gen.go", diff --git a/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql b/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql index dd3f59077..9bc035e46 100644 --- a/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql +++ b/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql @@ -38,20 +38,6 @@ begin; create_time wt_timestamp, update_time wt_timestamp, version wt_version, - plugin_name text not null - constraint plugin_name_must_be_not_empty - check(length(trim(plugin_name)) > 0) - constraint plugin_name_must_be_lowercase - check(lower(trim(plugin_name)) = plugin_name) - constraint plugin_host_plugin_name_uq - unique, - id_prefix text not null - constraint plugin_id_prefix_must_be_not_empty - check(length(trim(id_prefix)) > 0) - constraint plugin_id_prefix_must_fit_format - check (id_prefix ~ '^[a-z0-9]*$') - constraint plugin_host_id_prefix_uq - unique, constraint plugin_fkey foreign key (scope_id, public_id) references plugin(scope_id, public_id) @@ -71,7 +57,7 @@ begin; for each row execute procedure default_create_time(); create trigger immutable_columns before update on plugin_host - for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); + for each row execute procedure immutable_columns('public_id', 'create_time'); create trigger insert_plugin_subtype before insert on plugin_host for each row execute procedure insert_plugin_subtype(); diff --git a/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql b/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql index b1478e9d5..eafa2c5e8 100644 --- a/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql @@ -55,6 +55,7 @@ create view host_plugin_host_set_with_value_obj as select hs.public_id, hs.catalog_id, + hc.plugin_id, hs.name, hs.description, hs.create_time, @@ -65,8 +66,9 @@ select string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints from host_plugin_set hs - left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id -group by hs.public_id; + join host_plugin_catalog hc on hs.catalog_id = hc.public_id + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id +group by hs.public_id, hc.plugin_id; comment on view host_plugin_host_set_with_value_obj is 'host plugin host set with its associated value objects'; diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 1c3d02a05..5504c5ef4 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -7580,20 +7580,6 @@ alter table wh_session_connection_accumulating_fact create_time wt_timestamp, update_time wt_timestamp, version wt_version, - plugin_name text not null - constraint plugin_name_must_be_not_empty - check(length(trim(plugin_name)) > 0) - constraint plugin_name_must_be_lowercase - check(lower(trim(plugin_name)) = plugin_name) - constraint plugin_host_plugin_name_uq - unique, - id_prefix text not null - constraint plugin_id_prefix_must_be_not_empty - check(length(trim(id_prefix)) > 0) - constraint plugin_id_prefix_must_fit_format - check (id_prefix ~ '^[a-z0-9]*$') - constraint plugin_host_id_prefix_uq - unique, constraint plugin_fkey foreign key (scope_id, public_id) references plugin(scope_id, public_id) @@ -7613,7 +7599,7 @@ alter table wh_session_connection_accumulating_fact for each row execute procedure default_create_time(); create trigger immutable_columns before update on plugin_host - for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name'); + for each row execute procedure immutable_columns('public_id', 'create_time'); create trigger insert_plugin_subtype before insert on plugin_host for each row execute procedure insert_plugin_subtype(); @@ -7904,6 +7890,7 @@ create view host_plugin_host_set_with_value_obj as select hs.public_id, hs.catalog_id, + hc.plugin_id, hs.name, hs.description, hs.create_time, @@ -7914,8 +7901,9 @@ select string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints from host_plugin_set hs - left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id -group by hs.public_id; + join host_plugin_catalog hc on hs.catalog_id = hc.public_id + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id +group by hs.public_id, hc.plugin_id; comment on view host_plugin_host_set_with_value_obj is 'host plugin host set with its associated value objects'; `), diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 31bef3b4b..f3f8afa34 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -1244,6 +1244,13 @@ "schema": { "$ref": "#/definitions/controller.api.resources.hostcatalogs.v1.HostCatalog" } + }, + { + "name": "plugin_name", + "description": "As an alternative to providing the plugin id in the provided HostCatalog,\nthis field can be used to lookup the plugin using it's name.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4075,6 +4082,15 @@ "description": "Output only. Scope information for this resource.", "readOnly": true }, + "plugin_id": { + "type": "string", + "description": "The ID of the plugin of which this catalog is created." + }, + "plugin": { + "$ref": "#/definitions/controller.api.resources.plugins.v1.PluginInfo", + "description": "Output only. Plugin information for this resource.", + "readOnly": true + }, "name": { "type": "string", "description": "Optional name for identification purposes." @@ -4151,6 +4167,11 @@ "description": "Output only. Scope information for this resource.", "readOnly": true }, + "plugin": { + "$ref": "#/definitions/controller.api.resources.plugins.v1.PluginInfo", + "description": "Output only. Plugin information for this resource.", + "readOnly": true + }, "name": { "type": "string", "description": "Optional name for identification purposes." @@ -4220,6 +4241,11 @@ "description": "Output only. Scope information for this resource.", "readOnly": true }, + "plugin": { + "$ref": "#/definitions/controller.api.resources.plugins.v1.PluginInfo", + "description": "Output only. Plugin information for this resource.", + "readOnly": true + }, "name": { "type": "string", "description": "Optional name for identification purposes." @@ -4348,6 +4374,26 @@ }, "title": "ManagedGroup contains all fields related to an ManagedGroup resource" }, + "controller.api.resources.plugins.v1.PluginInfo": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Output only. The ID of the Plugin.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "Output only. The name of the plugin resource in boundary, if any.", + "readOnly": true + }, + "description": { + "type": "string", + "description": "Output only. The description of the plugin in boundary, if any.", + "readOnly": true + } + } + }, "controller.api.resources.roles.v1.Grant": { "type": "object", "properties": { diff --git a/internal/gen/controller/api/services/host_catalog_service.pb.go b/internal/gen/controller/api/services/host_catalog_service.pb.go index fb70dea13..7bb089e0d 100644 --- a/internal/gen/controller/api/services/host_catalog_service.pb.go +++ b/internal/gen/controller/api/services/host_catalog_service.pb.go @@ -234,6 +234,9 @@ type CreateHostCatalogRequest struct { unknownFields protoimpl.UnknownFields Item *hostcatalogs.HostCatalog `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + // As an alternative to providing the plugin id in the provided HostCatalog, + // this field can be used to lookup the plugin using it's name. + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` } func (x *CreateHostCatalogRequest) Reset() { @@ -275,6 +278,13 @@ func (x *CreateHostCatalogRequest) GetItem() *hostcatalogs.HostCatalog { return nil } +func (x *CreateHostCatalogRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + type CreateHostCatalogResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -566,111 +576,113 @@ var file_controller_api_services_v1_host_catalog_service_proto_rawDesc = []byte{ 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x22, 0x65, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, + 0x74, 0x65, 0x6d, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x78, 0x0a, + 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x49, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, + 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, + 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x19, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x78, 0x0a, 0x19, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3b, 0x0a, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x19, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1b, - 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xde, 0x07, 0x0a, 0x12, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, - 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x2e, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xde, 0x07, + 0x0a, 0x12, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, + 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, + 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x2e, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, + 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x92, + 0x41, 0x1f, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x2e, 0x12, 0xc2, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3a, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, - 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x92, 0x41, 0x1f, - 0x12, 0x1d, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, - 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x12, - 0xc2, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x40, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, - 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3a, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x32, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, - 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0xbb, - 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, - 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, - 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x42, 0x4d, 0x5a, 0x4b, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x24, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x12, 0xbb, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, + 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, + 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x42, 0x4d, + 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/gen/controller/api/services/host_catalog_service.pb.gw.go b/internal/gen/controller/api/services/host_catalog_service.pb.gw.go index e36256dae..e76c2dbc8 100644 --- a/internal/gen/controller/api/services/host_catalog_service.pb.gw.go +++ b/internal/gen/controller/api/services/host_catalog_service.pb.gw.go @@ -119,6 +119,10 @@ func local_request_HostCatalogService_ListHostCatalogs_0(ctx context.Context, ma } +var ( + filter_HostCatalogService_CreateHostCatalog_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_HostCatalogService_CreateHostCatalog_0(ctx context.Context, marshaler runtime.Marshaler, client HostCatalogServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CreateHostCatalogRequest var metadata runtime.ServerMetadata @@ -131,6 +135,13 @@ func request_HostCatalogService_CreateHostCatalog_0(ctx context.Context, marshal return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_CreateHostCatalog_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.CreateHostCatalog(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -148,6 +159,13 @@ func local_request_HostCatalogService_CreateHostCatalog_0(ctx context.Context, m return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_CreateHostCatalog_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.CreateHostCatalog(ctx, &protoReq) return msg, metadata, err diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go index f6559caf5..9a24404b1 100644 --- a/internal/host/plugin/host_catalog_secret_test.go +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -24,7 +24,7 @@ func TestHostCatalogSecret_New(t *testing.T) { kkms := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) type args struct { @@ -126,7 +126,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { kkms := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase) require.NoError(t, err) diff --git a/internal/host/plugin/host_catalog_test.go b/internal/host/plugin/host_catalog_test.go index 761dacb5a..cd7d90578 100644 --- a/internal/host/plugin/host_catalog_test.go +++ b/internal/host/plugin/host_catalog_test.go @@ -22,7 +22,7 @@ func TestHostCatalog_Create(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") type args struct { pluginId string @@ -149,7 +149,7 @@ func TestHostCatalog_Create(t *testing.T) { assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + id, err := newHostCatalogId(ctx) assert.NoError(t, err) tt.want.PublicId = id @@ -180,31 +180,31 @@ func TestHostCatalog_Create_DuplicateNames(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) _, prj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test1", "prefix1") - plg2 := host.TestPlugin(t, conn, "test2", "prefix2") + plg := host.TestPlugin(t, conn, "test1") + plg2 := host.TestPlugin(t, conn, "test2") got, err := NewHostCatalog(ctx, prj.GetPublicId(), plg.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx) require.NoError(t, err) require.NoError(t, w.Create(ctx, got)) // Can't create another resource with the same name in the same scope - got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) // Can't create another resource with same name in same scope even for different plugin got, err = NewHostCatalog(ctx, prj.GetPublicId(), plg2.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(ctx, plg2.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx) require.NoError(t, err) assert.Error(t, w.Create(ctx, got)) // Can create another resource with same name in different scope even for same plugin got, err = NewHostCatalog(ctx, prj2.GetPublicId(), plg.GetPublicId(), WithName("duplicate")) require.NoError(t, err) - got.PublicId, err = newHostCatalogId(ctx, plg.GetIdPrefix()) + got.PublicId, err = newHostCatalogId(ctx) require.NoError(t, err) assert.NoError(t, w.Create(ctx, got)) } @@ -213,7 +213,7 @@ func TestHostCatalog_Delete(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) ignoredCat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) _ = ignoredCat @@ -264,7 +264,7 @@ func TestHostCatalog_Delete_Cascading(t *testing.T) { t.Run("delete-scope", func(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "deletescope", "deletescope") + plg := host.TestPlugin(t, conn, "deletescope") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) deleted, err := w.Delete(ctx, prj) @@ -278,7 +278,7 @@ func TestHostCatalog_Delete_Cascading(t *testing.T) { t.Run("delete-plugin", func(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "deleteplugin", "deleteplugin") + plg := host.TestPlugin(t, conn, "deleteplugin") cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) deleted, err := w.Delete(ctx, plg) diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index ecfce316a..ac9a0d4cb 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -18,7 +18,7 @@ func TestHostSet_Create(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) @@ -168,7 +168,7 @@ func TestHostSet_Create(t *testing.T) { assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) - id, err := newHostSetId(ctx, plg.GetIdPrefix()) + id, err := newHostSetId(ctx) assert.NoError(t, err) tt.want.PublicId = id diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go index cb3d91e5b..fa384bded 100644 --- a/internal/host/plugin/ids.go +++ b/internal/host/plugin/ids.go @@ -2,7 +2,6 @@ package plugin import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -19,24 +18,22 @@ func init() { // PublicId prefixes for the resources in the plugin package. const ( // TODO: Pull these out of being constants and have them derivable at run time. - HostCatalogPrefix = "hcplg" - HostSetPrefix = "hsplg" + HostCatalogPrefix = "hc" + HostSetPrefix = "hs" Subtype = subtypes.Subtype("plugin") ) -func newHostCatalogId(ctx context.Context, upre string) (string, error) { - prefix := fmt.Sprintf("%s_%s", HostCatalogPrefix, upre) - id, err := db.NewPublicId(prefix) +func newHostCatalogId(ctx context.Context) (string, error) { + id, err := db.NewPublicId(HostCatalogPrefix) if err != nil { return "", errors.Wrap(ctx, err, "plugin.newHostCatalogId") } return id, nil } -func newHostSetId(ctx context.Context, upre string) (string, error) { - prefix := fmt.Sprintf("%s_%s", HostSetPrefix, upre) - id, err := db.NewPublicId(prefix) +func newHostSetId(ctx context.Context) (string, error) { + id, err := db.NewPublicId(HostSetPrefix) if err != nil { return "", errors.Wrap(ctx, err, "plugin.newHostSetId") } diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index 641c1b575..68f37ee36 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -24,8 +24,8 @@ func TestPluginCatalog_ImmutableFields(t *testing.T) { wrapper := db.TestWrapper(t) ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} o, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") - plg2 := host.TestPlugin(t, conn, "test2", "prefix2") + plg := host.TestPlugin(t, conn, "test") + plg2 := host.TestPlugin(t, conn, "test2") new := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) tests := []struct { @@ -100,7 +100,7 @@ func TestPluginSet_ImmutableFields(t *testing.T) { ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 0fc165c42..89cd3dbaf 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" - hostplg "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -28,55 +28,45 @@ import ( // not included in the returned *HostCatalog. // // Both c.CreateTime and c.UpdateTime are ignored. -func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Option) (*HostCatalog, error) { +func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Option) (*HostCatalog, *hostplugin.Plugin, error) { const op = "plugin.(Repository).CreateCatalog" if c == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostCatalog") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostCatalog") } if c.HostCatalog == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostCatalog") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostCatalog") } if c.ScopeId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } if c.PublicId != "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") } if c.PluginId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin id") } if c.Attributes == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") } c = c.clone() - - plg := hostplg.NewPlugin("", "") - plg.PublicId = c.PluginId - if err := r.reader.LookupByPublicId(ctx, plg); err != nil { - if errors.IsNotFoundError(err) { - return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter), errors.WithMsg(fmt.Sprintf("can't find plugin with id: %q", c.GetPluginId()))) - } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get host plugin with id %q", c.PluginId))) - } - - id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + id, err := newHostCatalogId(ctx) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } c.PublicId = id - plgClient, ok := r.plugins[plg.GetPublicId()] + plgClient, ok := r.plugins[c.GetPluginId()] if !ok || plgClient == nil { - return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", plg.GetPluginName())) + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) } plgHc, err := toPluginCatalog(ctx, c) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } plgResp, err := plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc}) if err != nil { if status.Code(err) != codes.Unimplemented { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } } @@ -84,20 +74,20 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetSecrets()) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) } if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } } oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } var newHostCatalog *HostCatalog @@ -142,39 +132,47 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt if err != nil { if errors.IsUniqueError(err) { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", c.ScopeId, c.Name))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", c.ScopeId, c.Name))) } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", c.ScopeId))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", c.ScopeId))) } - return newHostCatalog, nil + plg, err := r.getPlugin(ctx, newHostCatalog.GetPluginId()) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + return newHostCatalog, plg, nil } // LookupCatalog returns the HostCatalog for id. Returns nil, nil if no // HostCatalog is found for id. -func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) (*HostCatalog, error) { +func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) (*HostCatalog, *hostplugin.Plugin, error) { const op = "plugin.(Repository).LookupCatalog" if id == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } c, err := r.getCatalog(ctx, id) if err != nil { if errors.IsNotFoundError(err) { - return nil, nil + return nil, nil, nil } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) } - return c, nil + plg, err := r.getPlugin(ctx, c.GetPluginId()) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + return c, plg, nil } // ListCatalogs returns a slice of HostCatalogs for the scope IDs. WithLimit is the only option supported. -func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ...host.Option) ([]*HostCatalog, error) { +func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ...host.Option) ([]*HostCatalog, []*hostplugin.Plugin, error) { const op = "plugin.(Repository).ListCatalogs" if len(scopeIds) == 0 { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } opts, err := host.GetOpts(opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } limit := r.defaultLimit if opts.WithLimit != 0 { @@ -183,9 +181,17 @@ func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt .. } var hostCatalogs []*HostCatalog if err := r.reader.SearchWhere(ctx, &hostCatalogs, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)); err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } - return hostCatalogs, nil + plgIds := make([]string, 0, len(hostCatalogs)) + for _, c := range hostCatalogs { + plgIds = append(plgIds, c.PluginId) + } + var plgs []*hostplugin.Plugin + if err := r.reader.SearchWhere(ctx, &plgs, "public_id in (?)", []interface{}{plgIds}); err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + return hostCatalogs, plgs, nil } // getCatalog retrieves the *HostCatalog with the provided id. If it is not found or there @@ -200,6 +206,16 @@ func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, e return c, nil } +func (r *Repository) getPlugin(ctx context.Context, id string) (*hostplugin.Plugin, error) { + const op = "plugin.(Repository).getPlugin" + c := hostplugin.NewPlugin() + c.PublicId = id + if err := r.reader.LookupByPublicId(ctx, c); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + } + return c, nil +} + // getPersistedDataForCatalog returns the persisted data for a catalog if // present. c must have a valid Public Id and Scope Id set. // TODO: consider merging the functions for getting catalog and persisted data into a view. diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index e3f8ed94a..b4cc8c764 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -28,8 +28,8 @@ func TestRepository_CreateCatalog(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := hostplg.TestPlugin(t, conn, "test", "test") - unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented") + plg := hostplg.TestPlugin(t, conn, "test") + unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") // gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the // test against the expected values sent to the plugin. @@ -250,7 +250,7 @@ func TestRepository_CreateCatalog(t *testing.T) { repo, err := NewRepository(rw, rw, kmsCache, plgm) assert.NoError(err) assert.NotNil(repo) - got, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) + got, _, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -311,7 +311,7 @@ func TestRepository_CreateCatalog(t *testing.T) { }, } - got, err := repo.CreateCatalog(context.Background(), in) + got, _, err := repo.CreateCatalog(context.Background(), in) assert.NoError(err) assert.NotNil(got) assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) @@ -320,7 +320,7 @@ func TestRepository_CreateCatalog(t *testing.T) { assert.Equal(in.Description, got.Description) assert.Equal(got.CreateTime, got.UpdateTime) - got2, err := repo.CreateCatalog(context.Background(), in) + got2, _, err := repo.CreateCatalog(context.Background(), in) assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) assert.Nil(got2) }) @@ -342,7 +342,7 @@ func TestRepository_CreateCatalog(t *testing.T) { in2 := in.clone() in.ScopeId = prj.GetPublicId() - got, err := repo.CreateCatalog(context.Background(), in) + got, _, err := repo.CreateCatalog(context.Background(), in) assert.NoError(err) assert.NotNil(got) assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) @@ -352,7 +352,7 @@ func TestRepository_CreateCatalog(t *testing.T) { assert.Equal(got.CreateTime, got.UpdateTime) in2.ScopeId = org.GetPublicId() - got2, err := repo.CreateCatalog(context.Background(), in2) + got2, _, err := repo.CreateCatalog(context.Background(), in2) assert.NoError(err) assert.NotNil(got2) assertPluginBasedPublicId(t, HostCatalogPrefix, got2.PublicId) @@ -367,7 +367,7 @@ func assertPluginBasedPublicId(t *testing.T, prefix, actual string) { t.Helper() assert.NotEmpty(t, actual) parts := strings.Split(actual, "_") - assert.Equalf(t, 3, len(parts), "want two '_' in PublicId, got %d in %q", len(parts), actual) + assert.Equalf(t, 2, len(parts), "want 1 '_' in PublicId, got %d in %q", len(parts), actual) assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) } @@ -377,12 +377,12 @@ func TestRepository_LookupCatalog(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := hostplg.TestPlugin(t, conn, "test", "test") + plg := hostplg.TestPlugin(t, conn, "test") plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, } cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - badId, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + badId, err := newHostCatalogId(ctx) assert.NoError(t, err) assert.NotNil(t, badId) @@ -419,7 +419,7 @@ func TestRepository_LookupCatalog(t *testing.T) { assert.NoError(err) assert.NotNil(repo) - got, err := repo.LookupCatalog(ctx, tt.id) + got, _, err := repo.LookupCatalog(ctx, tt.id) if tt.wantErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) return @@ -443,7 +443,7 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { wrapper := db.TestWrapper(t) rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) - plg := hostplg.TestPlugin(t, conn, "test", "test") + plg := hostplg.TestPlugin(t, conn, "test") plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, } @@ -463,7 +463,8 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { } } - got, err := repo.ListCatalogs(context.Background(), projs) + got, plgs, err := repo.ListCatalogs(context.Background(), projs) require.NoError(t, err) assert.Equal(t, total, len(got)) + assert.Contains(t, plgs, plg) } diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index a189331bb..72e58ecd4 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" - hostplg "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -29,70 +29,64 @@ import ( // // Both s.Name and s.Description are optional. If s.Name is set, it must be // unique within s.CatalogId. -func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, _ ...Option) (*HostSet, error) { +func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, _ ...Option) (*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).CreateSet" if s == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostSet") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostSet") } if s.HostSet == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostSet") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostSet") } if s.CatalogId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") } if s.PublicId != "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") } if scopeId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } if s.Attributes == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") } s = s.clone() c, err := r.getCatalog(ctx, s.CatalogId) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog")) } per, err := r.getPersistedDataForCatalog(ctx, c) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up persisted data")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up persisted data")) } - - plg := hostplg.NewPlugin("", "") - plg.PublicId = c.GetPluginId() - if err := r.reader.LookupByPublicId(ctx, plg); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host plugin")) - } - - id, err := newHostSetId(ctx, plg.GetIdPrefix()) + id, err := newHostSetId(ctx) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } s.PublicId = id - plgClient, ok := r.plugins[plg.GetPublicId()] + plgClient, ok := r.plugins[c.GetPluginId()] if !ok || plgClient == nil { - return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin with plugin name %q not available", plg.GetPluginName())) + return nil, nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) } + plgHc, err := toPluginCatalog(ctx, c) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } plgHs, err := toPluginSet(ctx, s) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } if _, err := plgClient.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{Catalog: plgHc, Set: plgHs, Persisted: per}); err != nil { if status.Code(err) != codes.Unimplemented { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } } oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } var preferredEndpoints []interface{} @@ -101,7 +95,7 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, for i, e := range s.PreferredEndpoints { obj, err := host.NewPreferredEndpoint(ctx, s.PublicId, uint32(i+1), e) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } preferredEndpoints = append(preferredEndpoints, obj) } @@ -146,67 +140,71 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, if err != nil { if errors.IsUniqueError(err) { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s: name %s already exists", s.CatalogId, s.Name))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s: name %s already exists", s.CatalogId, s.Name))) } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId))) } - return returnedHostSet, nil + plg, err := r.getPlugin(ctx, c.GetPluginId()) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + return returnedHostSet, plg, nil } // LookupSet will look up a host set in the repository and return the host // set. If the host set is not found, it will return nil, nil. // All options are ignored. -func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, error) { +func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).LookupSet" if publicId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - sets, err := r.getSets(ctx, publicId, "", opt...) + sets, plg, err := r.getSets(ctx, publicId, "", opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } switch { case len(sets) == 0: - return nil, nil // not an error to return no rows for a "lookup" + return nil, nil, nil // not an error to return no rows for a "lookup" case len(sets) > 1: - return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) + return nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) default: - return sets[0], nil + return sets[0], plg, nil } } // ListSets returns a slice of HostSets for the catalogId. WithLimit is the // only option supported. -func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host.Option) ([]*HostSet, error) { +func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host.Option) ([]*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).ListSets" if catalogId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "missing catalog id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing catalog id") } - sets, err := r.getSets(ctx, "", catalogId, opt...) + sets, plg, err := r.getSets(ctx, "", catalogId, opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } - return sets, nil + return sets, plg, nil } -func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, error) { +func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).getSets" const aggregateDelimiter = "|" const priorityDelimiter = "=" if publicId == "" && catalogId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "missing search criteria: both host set id and catalog id are empty") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing search criteria: both host set id and catalog id are empty") } if publicId != "" && catalogId != "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "searching for both a host set id and a catalog id is not supported") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "searching for both a host set id and a catalog id is not supported") } opts, err := host.GetOpts(opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } limit := r.defaultLimit @@ -237,12 +235,13 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str var aggHostSets []*hostSetAgg if err := r.reader.SearchWhere(ctx, &aggHostSets, where, args, dbArgs...); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", publicId))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", publicId))) } if len(aggHostSets) == 0 { - return nil, nil + return nil, nil, nil } + plgId := aggHostSets[0].PluginId sets := make([]*HostSet, 0, len(aggHostSets)) for _, agg := range aggHostSets { @@ -285,7 +284,7 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str return indexi < indexj }) if sortErr != nil { - return nil, sortErr + return nil, nil, sortErr } for i, ep := range eps { // At this point they're in the correct order, but we still @@ -298,8 +297,15 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str } sets = append(sets, hs) } + var plg *hostplugin.Plugin + if plgId != "" { + plg, err = r.getPlugin(ctx, plgId) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + } - return sets, nil + return sets, plg, nil } // hostSetAgg is a view that aggregates the host set's value objects in to diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index c32c52853..3ffb12142 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -31,8 +31,8 @@ func TestRepository_CreateSet(t *testing.T) { kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) - plg := hostplg.TestPlugin(t, conn, "create", "create") - unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented") + plg := hostplg.TestPlugin(t, conn, "create") + unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") var pluginReceivedAttrs *structpb.Struct plgm := map[string]plgpb.HostPluginServiceServer{ @@ -209,7 +209,7 @@ func TestRepository_CreateSet(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm) require.NoError(err) require.NotNil(repo) - got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) + got, plgInfo, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -226,6 +226,7 @@ func TestRepository_CreateSet(t *testing.T) { wantedPluginAttributes := &structpb.Struct{} require.NoError(proto.Unmarshal(tt.want.Attributes, wantedPluginAttributes)) assert.Empty(cmp.Diff(wantedPluginAttributes, pluginReceivedAttrs, protocmp.Transform())) + assert.Empty(cmp.Diff(plgInfo, plg, protocmp.Transform())) assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) }) @@ -248,7 +249,7 @@ func TestRepository_CreateSet(t *testing.T) { }, } - got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + got, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) require.NoError(err) require.NotNil(got) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) @@ -257,7 +258,7 @@ func TestRepository_CreateSet(t *testing.T) { assert.Equal(in.Description, got.GetDescription()) assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) - got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + got2, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) assert.Nil(got2) }) @@ -281,7 +282,7 @@ func TestRepository_CreateSet(t *testing.T) { in2 := in.clone() in.CatalogId = catalogA.PublicId - got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + got, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) require.NoError(err) require.NotNil(got) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) @@ -291,7 +292,7 @@ func TestRepository_CreateSet(t *testing.T) { assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) in2.CatalogId = catalogB.PublicId - got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in2) + got2, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in2) require.NoError(err) require.NotNil(got2) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) @@ -310,7 +311,7 @@ func TestRepository_LookupSet(t *testing.T) { kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) - plg := hostplg.TestPlugin(t, conn, "lookup", "lookup") + plg := hostplg.TestPlugin(t, conn, "lookup") plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, } @@ -319,7 +320,7 @@ func TestRepository_LookupSet(t *testing.T) { hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, }) - hostSetId, err := newHostSetId(ctx, plg.GetIdPrefix()) + hostSetId, err := newHostSetId(ctx) require.NoError(t, err) tests := []struct { @@ -350,7 +351,7 @@ func TestRepository_LookupSet(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) - got, err := repo.LookupSet(ctx, tt.in) + got, _, err := repo.LookupSet(ctx, tt.in) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -372,7 +373,7 @@ func TestRepository_ListSets(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) - plg := hostplg.TestPlugin(t, conn, "list", "list") + plg := hostplg.TestPlugin(t, conn, "list") plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, } @@ -385,6 +386,8 @@ func TestRepository_ListSets(t *testing.T) { TestSet(t, conn, kms, catalogA, plgm), } + printoutTable(t, rw) + tests := []struct { name string in string @@ -415,7 +418,7 @@ func TestRepository_ListSets(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) - got, err := repo.ListSets(context.Background(), tt.in, tt.opts...) + got, gotPlg, err := repo.ListSets(context.Background(), tt.in, tt.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -427,6 +430,9 @@ func TestRepository_ListSets(t *testing.T) { protocmp.Transform(), } assert.Empty(cmp.Diff(tt.want, got, opts...)) + if got != nil { + assert.Empty(cmp.Diff(plg, gotPlg, protocmp.Transform())) + } }) } } @@ -439,7 +445,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) - plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit") + plg := hostplg.TestPlugin(t, conn, "listlimit") plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &TestPluginServer{}, } @@ -501,9 +507,19 @@ func TestRepository_ListSets_Limits(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm, tt.repoOpts...) assert.NoError(err) require.NotNil(repo) - got, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...) + got, gotPlg, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...) require.NoError(err) assert.Len(got, tt.wantLen) + assert.Empty(cmp.Diff(plg, gotPlg, protocmp.Transform())) }) } } + +func printoutTable(t *testing.T, rw *db.Db) { + ctx := context.Background() + hsas := []*hostSetAgg{} + require.NoError(t, rw.SearchWhere(ctx, &hsas, "", nil)) + for _, hs := range hsas { + t.Logf("%#v", hs) + } +} diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index ed4c502df..a52814fd1 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -25,11 +25,11 @@ func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...O require.NoError(t, err) assert.NotNil(t, cat) - plg := host.NewPlugin("", "") + plg := host.NewPlugin() plg.PublicId = pluginId require.NoError(t, w.LookupByPublicId(ctx, plg)) - id, err := newHostCatalogId(ctx, plg.GetIdPrefix()) + id, err := newHostCatalogId(ctx) assert.NoError(t, err) assert.NotEmpty(t, id) cat.PublicId = id @@ -54,15 +54,15 @@ func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, pl require.NoError(err) require.NotNil(set) - plg := host.NewPlugin("", "") + plg := host.NewPlugin() plg.PublicId = hc.GetPluginId() require.NoError(rw.LookupByPublicId(ctx, plg)) - id, err := newHostSetId(ctx, plg.GetIdPrefix()) + id, err := newHostSetId(ctx) require.NoError(err) require.NotEmpty(id) - set, err = repo.CreateSet(ctx, hc.ScopeId, set, opt...) + set, _, err = repo.CreateSet(ctx, hc.ScopeId, set, opt...) require.NoError(err) return set diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index d5b7efbf1..713869669 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -1,8 +1,6 @@ package plugin import ( - "fmt" - "strings" "testing" "github.com/hashicorp/boundary/internal/db" @@ -22,13 +20,13 @@ func Test_TestCatalogs(t *testing.T) { require.NotNil(proj) assert.NotEmpty(proj.GetPublicId()) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") require.NotNil(plg) assert.NotEmpty(plg.GetPublicId()) cs := TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId(), WithName("foo"), WithDescription("bar")) assert.NotEmpty(cs.GetPublicId()) - assert.True(strings.HasPrefix(cs.GetPublicId(), fmt.Sprintf("%s_%s", HostCatalogPrefix, "prefix"))) + db.AssertPublicId(t, HostCatalogPrefix, cs.GetPublicId()) assert.Equal("foo", cs.GetName()) assert.Equal("bar", cs.GetDescription()) } @@ -42,14 +40,14 @@ func Test_TestSet(t *testing.T) { require.NotNil(prj) assert.NotEmpty(prj.GetPublicId()) - plg := host.TestPlugin(t, conn, "test", "prefix") + plg := host.TestPlugin(t, conn, "test") require.NotNil(plg) assert.NotEmpty(plg.GetPublicId()) c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceServer{plg.GetPublicId(): &TestPluginServer{}}, WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) - assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix"))) + db.AssertPublicId(t, HostSetPrefix, set.GetPublicId()) assert.Equal("foo", set.GetName()) assert.Equal("bar", set.GetDescription()) } diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go index aaaca17e7..738a16b40 100644 --- a/internal/host/preferred_endpoint_test.go +++ b/internal/host/preferred_endpoint_test.go @@ -25,7 +25,7 @@ func TestPreferredEndpoint_Create(t *testing.T) { kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := hostplg.TestPlugin(t, conn, "create", "create") + plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -155,7 +155,7 @@ func TestPreferredEndpoint_Delete(t *testing.T) { kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - plg := hostplg.TestPlugin(t, conn, "create", "create") + plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, diff --git a/internal/perms/output_fields.go b/internal/perms/output_fields.go index 9f6a497e1..b50a39d88 100644 --- a/internal/perms/output_fields.go +++ b/internal/perms/output_fields.go @@ -71,6 +71,8 @@ func (o OutputFieldsMap) SelfOrDefaults(userId string) OutputFieldsMap { globals.IdField: true, globals.ScopeField: true, globals.ScopeIdField: true, + globals.PluginIdField: true, + globals.PluginField: true, globals.NameField: true, globals.DescriptionField: true, globals.TypeField: true, diff --git a/internal/perms/output_fields_test.go b/internal/perms/output_fields_test.go index a2f2b96e0..7a7080559 100644 --- a/internal/perms/output_fields_test.go +++ b/internal/perms/output_fields_test.go @@ -253,6 +253,8 @@ func Test_ACLSelfOrDefault(t *testing.T) { globals.IdField: true, globals.ScopeField: true, globals.ScopeIdField: true, + globals.PluginField: true, + globals.PluginIdField: true, globals.NameField: true, globals.DescriptionField: true, globals.TypeField: true, diff --git a/internal/plugin/host/ids.go b/internal/plugin/host/ids.go index c042b3c2a..5aafccb31 100644 --- a/internal/plugin/host/ids.go +++ b/internal/plugin/host/ids.go @@ -7,7 +7,7 @@ import ( // PublicId prefixes for the resources in the plugin package. const ( - PluginPrefix = "plgh" + PluginPrefix = "pl" ) func newPluginId() (string, error) { diff --git a/internal/plugin/host/immutable_fields_test.go b/internal/plugin/host/immutable_fields_test.go index d4e48b6e8..ebb2faf3b 100644 --- a/internal/plugin/host/immutable_fields_test.go +++ b/internal/plugin/host/immutable_fields_test.go @@ -19,7 +19,7 @@ func TestHostPlugin_ImmutableFields(t *testing.T) { w := db.New(conn) ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} - plg := TestPlugin(t, conn, "test", "prefix") + plg := TestPlugin(t, conn, "test") newPlugin := plg @@ -46,15 +46,6 @@ func TestHostPlugin_ImmutableFields(t *testing.T) { }(), fieldMask: []string{"CreateTime"}, }, - { - name: "pluginName", - update: func() *Plugin { - c := newPlugin.testClonePlugin() - c.PluginName = "different pluginName" - return c - }(), - fieldMask: []string{"PluginName"}, - }, } for _, tt := range tests { tt := tt diff --git a/internal/plugin/host/plugin.go b/internal/plugin/host/plugin.go index b6a205ca7..a7ef7c9f5 100644 --- a/internal/plugin/host/plugin.go +++ b/internal/plugin/host/plugin.go @@ -16,12 +16,10 @@ type Plugin struct { // NewPlugin creates a new in memory Plugin assigned to the global scope. // Name, Description are the only allowed option. All other options are ignored. -func NewPlugin(pluginName, idPrefix string, opt ...Option) *Plugin { +func NewPlugin(opt ...Option) *Plugin { opts := getOpts(opt...) p := &Plugin{ Plugin: &store.Plugin{ - PluginName: pluginName, - IdPrefix: idPrefix, ScopeId: scope.Global.String(), Name: opts.withName, Description: opts.withDescription, diff --git a/internal/plugin/host/plugin_test.go b/internal/plugin/host/plugin_test.go index a70476494..0bd6a3c30 100644 --- a/internal/plugin/host/plugin_test.go +++ b/internal/plugin/host/plugin_test.go @@ -15,168 +15,50 @@ import ( func TestPlugin_Create(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") - type args struct { - pluginName string - idPrefix string - opts []Option - } + type args struct{} tests := []struct { name string - args args + opts []Option want *Plugin wantErr bool }{ { - name: "blank-pluginName", - args: args{ - idPrefix: "prefix", - }, + name: "valid", want: &Plugin{ Plugin: &store.Plugin{ - IdPrefix: "prefix", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "blank-idPrefix", - args: args{ - pluginName: "plugin name", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "plugin name", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "idprefix-capitalized", - args: args{ - pluginName: "idprefixcapitalized", - idPrefix: "IdPrefixCapitalized", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "idprefixcapitalized", - IdPrefix: "IdPrefixCapitalized", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "idprefix-space", - args: args{ - pluginName: "idprefix space", - idPrefix: "idprefix space", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "idprefix space", - IdPrefix: "idprefix space", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "pluginName-capitalized", - args: args{ - pluginName: "PluginNameCapitalized", - idPrefix: "pluginnamecapitalized", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "PluginNameCapitalized", - IdPrefix: "pluginnamecapitalized", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "valid-no-options", - args: args{ - pluginName: "validnooptions", - idPrefix: "validnooptions", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "validnooptions", - IdPrefix: "validnooptions", - ScopeId: scope.Global.String(), + ScopeId: scope.Global.String(), }, }, }, { - name: "valid-with-description", - args: args{ - pluginName: "validwithdescription", - idPrefix: "validwithdescription", - opts: []Option{ - WithDescription("description"), - }, - }, + name: "with-name", + opts: []Option{WithName("foo")}, want: &Plugin{ Plugin: &store.Plugin{ - PluginName: "validwithdescription", - IdPrefix: "validwithdescription", + Name: "foo", + ScopeId: scope.Global.String(), + }, + }, + }, + { + name: "with-description", + opts: []Option{WithDescription("foo")}, + want: &Plugin{ + Plugin: &store.Plugin{ + Description: "foo", ScopeId: scope.Global.String(), - Description: "description", - }, - }, - }, - { - name: "valid-pluginName-name-option", - args: args{ - pluginName: "validpluginnamenameoption", - idPrefix: "validpluginnamenameoption", - opts: []Option{ - WithName("valid-pluginName-name-option"), - WithDescription("description"), - }, - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "validpluginnamenameoption", - IdPrefix: "validpluginnamenameoption", - ScopeId: scope.Global.String(), - Name: "valid-pluginName-name-option", - Description: "description", }, }, }, // This must be run after the "valid-no-options" test { - name: "duplicate-pluginName", - args: args{ - pluginName: "validnooptions", - idPrefix: "duplicatepluginname", - }, + name: "duplicate-name", + opts: []Option{WithName("foo")}, want: &Plugin{ Plugin: &store.Plugin{ - PluginName: "validnooptions", - IdPrefix: "duplicatepluginname", - ScopeId: scope.Global.String(), - }, - }, - wantErr: true, - }, - { - name: "duplicate-idPrefix", - args: args{ - pluginName: "duplicateidprefix", - idPrefix: "validnooptions", - }, - want: &Plugin{ - Plugin: &store.Plugin{ - PluginName: "duplicateidprefix", - IdPrefix: "validnooptions", - ScopeId: scope.Global.String(), + Name: "foo", + ScopeId: scope.Global.String(), }, }, wantErr: true, @@ -186,7 +68,7 @@ func TestPlugin_Create(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - got := NewPlugin(tt.args.pluginName, tt.args.idPrefix, tt.args.opts...) + got := NewPlugin(tt.opts...) require.NotNil(t, got) require.Emptyf(t, got.PublicId, "PublicId set") @@ -212,7 +94,7 @@ func TestPlugin_Update(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") w := db.New(conn) ctx := context.Background() - plg := TestPlugin(t, conn, "delete-plugin", "prefix") + plg := TestPlugin(t, conn, "delete-plugin") assert.Equal(t, uint32(1), plg.Version) plg.Name = "New" @@ -230,7 +112,7 @@ func TestPlugin_Delete(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") w := db.New(conn) ctx := context.Background() - plg := TestPlugin(t, conn, "delete-plugin", "prefix") + plg := TestPlugin(t, conn, "delete-plugin") deleted, err := w.Delete(ctx, plg) require.NoError(t, err) @@ -261,7 +143,7 @@ func TestPlugin_SetTableName(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - def := NewPlugin("", "prefix") + def := NewPlugin() require.Equal(defaultTableName, def.TableName()) s := &Plugin{ Plugin: &store.Plugin{}, diff --git a/internal/plugin/host/repository_plugin.go b/internal/plugin/host/repository_plugin.go index 574ade970..e3669fefa 100644 --- a/internal/plugin/host/repository_plugin.go +++ b/internal/plugin/host/repository_plugin.go @@ -16,9 +16,6 @@ import ( // contain a valid ScopeID. p must not contain a PublicId. The PublicId is // generated and assigned by this method. opt is ignored. // -// Both p.IdPrefix and p.PluginName are required and must match. The -// p.IdPrefix must contain only lower case alphanumeric characters. -// // Both p.Name and p.Description are optional. If p.Name is set, it must be // unique within p.ScopeID. // @@ -40,17 +37,6 @@ func (r *Repository) CreatePlugin(ctx context.Context, p *Plugin, _ ...Option) ( if p.PublicId != "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") } - if p.PluginName == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin name") - } - if p.IdPrefix == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no id prefix") - } - // TODO: remove the restriction on prefix having to be the same as plugin name - if p.PluginName != p.IdPrefix { - return nil, errors.New(ctx, errors.InvalidParameter, op, "id prefix and plugin name don't match") - } - p = p.clone() id, err := newPluginId() @@ -112,16 +98,16 @@ func (r *Repository) LookupPlugin(ctx context.Context, id string, _ ...Option) ( return c, nil } -// LookupPluginByPluginName returns the Plugin for a given name. Returns nil, nil if no +// LookupPluginByName returns the Plugin for a given name. Returns nil, nil if no // Plugin is found with that plugin name. -func (r *Repository) LookupPluginByPluginName(ctx context.Context, name string, _ ...Option) (*Plugin, error) { - const op = "host.(Repository).LookupPluginByPluginName" +func (r *Repository) LookupPluginByName(ctx context.Context, name string, _ ...Option) (*Plugin, error) { + const op = "host.(Repository).LookupPluginByName" if name == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin name") } p := allocPlugin() - if err := r.reader.LookupWhere(ctx, p, "plugin_name=?", name); err != nil { + if err := r.reader.LookupWhere(ctx, p, "name=?", name); err != nil { if errors.IsNotFoundError(err) { return nil, nil } diff --git a/internal/plugin/host/repository_plugin_test.go b/internal/plugin/host/repository_plugin_test.go index 262aaf99c..2ec31fa46 100644 --- a/internal/plugin/host/repository_plugin_test.go +++ b/internal/plugin/host/repository_plugin_test.go @@ -41,39 +41,18 @@ func TestRepository_CreatePlugin(t *testing.T) { in: &Plugin{}, wantIsErr: errors.InvalidParameter, }, - { - name: "valid-no-options", - in: &Plugin{ - Plugin: &store.Plugin{ - ScopeId: scope.Global.String(), - IdPrefix: "validnooptions", - PluginName: "validnooptions", - }, - }, - want: &Plugin{ - Plugin: &store.Plugin{ - ScopeId: scope.Global.String(), - IdPrefix: "validnooptions", - PluginName: "validnooptions", - }, - }, - }, { name: "valid-with-name", in: &Plugin{ Plugin: &store.Plugin{ - Name: "test-name-repo", - ScopeId: scope.Global.String(), - IdPrefix: "validwithname", - PluginName: "validwithname", + Name: "test-name-repo", + ScopeId: scope.Global.String(), }, }, want: &Plugin{ Plugin: &store.Plugin{ - Name: "test-name-repo", - ScopeId: scope.Global.String(), - IdPrefix: "validwithname", - PluginName: "validwithname", + Name: "test-name-repo", + ScopeId: scope.Global.String(), }, }, }, @@ -83,37 +62,20 @@ func TestRepository_CreatePlugin(t *testing.T) { Plugin: &store.Plugin{ Description: "test-description-repo", ScopeId: scope.Global.String(), - IdPrefix: "validwithdescription", - PluginName: "validwithdescription", }, }, want: &Plugin{ Plugin: &store.Plugin{ Description: "test-description-repo", ScopeId: scope.Global.String(), - IdPrefix: "validwithdescription", - PluginName: "validwithdescription", }, }, }, - { - name: "mismatching-prefix-plugin-name", - in: &Plugin{ - Plugin: &store.Plugin{ - ScopeId: scope.Global.String(), - IdPrefix: "foo", - PluginName: "bar", - }, - }, - wantIsErr: errors.InvalidParameter, - }, { name: "non-global-scope", in: &Plugin{ Plugin: &store.Plugin{ - ScopeId: "o_1234567890", - IdPrefix: "foo", - PluginName: "foo", + ScopeId: "o_1234567890", }, }, wantIsErr: errors.InvalidParameter, @@ -150,38 +112,8 @@ func TestRepository_CreatePlugin(t *testing.T) { assert.NotNil(repo) in := &Plugin{ Plugin: &store.Plugin{ - ScopeId: scope.Global.String(), - IdPrefix: "invalidduplicatenames", - PluginName: "invalidduplicatenames", - Name: "invalid-duplicate-names", - }, - } - - got, err := repo.CreatePlugin(context.Background(), in) - assert.NoError(err) - assert.NotNil(got) - assertPublicId(t, PluginPrefix, got.PublicId) - assert.NotSame(in, got) - assert.Equal(in.Name, got.Name) - assert.Equal(in.Description, got.Description) - assert.Equal(got.CreateTime, got.UpdateTime) - - got2, err := repo.CreatePlugin(context.Background(), in) - assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) - assert.Nil(got2) - }) - - t.Run("invalid-duplicate-plugin-names-prefixes", func(t *testing.T) { - assert := assert.New(t) - kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) - assert.NoError(err) - assert.NotNil(repo) - in := &Plugin{ - Plugin: &store.Plugin{ - ScopeId: scope.Global.String(), - IdPrefix: "invalidduplicatepluginnames", - PluginName: "invalidduplicatepluginnames", + ScopeId: scope.Global.String(), + Name: "invalid-duplicate-names", }, } @@ -212,7 +144,7 @@ func TestRepository_LookupPlugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - plg := TestPlugin(t, conn, "test", "test") + plg := TestPlugin(t, conn, "test") badId, err := newPluginId() assert.NoError(t, err) assert.NotNil(t, badId) @@ -268,11 +200,11 @@ func TestRepository_LookupPlugin(t *testing.T) { } } -func TestRepository_LookupPluginByPluginName(t *testing.T) { +func TestRepository_LookupPluginByName(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - plg := TestPlugin(t, conn, "name123", "name123") + plg := TestPlugin(t, conn, "name123") tests := []struct { name string @@ -282,7 +214,7 @@ func TestRepository_LookupPluginByPluginName(t *testing.T) { }{ { name: "found", - pluginName: plg.GetPluginName(), + pluginName: plg.GetName(), want: plg, }, { @@ -307,7 +239,7 @@ func TestRepository_LookupPluginByPluginName(t *testing.T) { assert.NoError(err) assert.NotNil(repo) - got, err := repo.LookupPluginByPluginName(context.Background(), tt.pluginName) + got, err := repo.LookupPluginByName(context.Background(), tt.pluginName) if tt.wantErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) return diff --git a/internal/plugin/host/store/plugin.pb.go b/internal/plugin/host/store/plugin.pb.go index ac2cade04..413893e11 100644 --- a/internal/plugin/host/store/plugin.pb.go +++ b/internal/plugin/host/store/plugin.pb.go @@ -49,12 +49,6 @@ type Plugin struct { ScopeId string `protobuf:"bytes,60,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` - // plugin_name is required. The value must be unique across all host plugins - // @inject_tag: `gorm:"default:null"` - PluginName string `protobuf:"bytes,80,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty" gorm:"default:null"` - // id_prefix is required. The value must be unique across all host plugins - // @inject_tag: `gorm:"default:null"` - IdPrefix string `protobuf:"bytes,90,opt,name=id_prefix,json=idPrefix,proto3" json:"id_prefix,omitempty" gorm:"default:null"` } func (x *Plugin) Reset() { @@ -138,20 +132,6 @@ func (x *Plugin) GetVersion() uint32 { return 0 } -func (x *Plugin) GetPluginName() string { - if x != nil { - return x.PluginName - } - return "" -} - -func (x *Plugin) GetIdPrefix() string { - if x != nil { - return x.IdPrefix - } - return "" -} - var File_controller_storage_plugin_host_store_v1_plugin_proto protoreflect.FileDescriptor var file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = []byte{ @@ -166,7 +146,7 @@ var file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = []byte{ 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, @@ -188,16 +168,12 @@ var file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = []byte{ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x69, 0x64, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x40, 0x5a, 0x3e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, + 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/plugin/host/testing.go b/internal/plugin/host/testing.go index ff585ed4b..2bc6fa35d 100644 --- a/internal/plugin/host/testing.go +++ b/internal/plugin/host/testing.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/require" ) -func TestPlugin(t *testing.T, conn *gorm.DB, name string, prefix string) *Plugin { +func TestPlugin(t *testing.T, conn *gorm.DB, name string) *Plugin { t.Helper() - p := NewPlugin(name, prefix) + p := NewPlugin(WithName(name)) id, err := newPluginId() require.NoError(t, err) p.PublicId = id diff --git a/internal/plugin/host/testing_test.go b/internal/plugin/host/testing_test.go index 916883773..f72a7f755 100644 --- a/internal/plugin/host/testing_test.go +++ b/internal/plugin/host/testing_test.go @@ -12,7 +12,7 @@ func Test_TestPlugins(t *testing.T) { assert, require := assert.New(t), require.New(t) conn, _ := db.TestSetup(t, "postgres") - plg := TestPlugin(t, conn, "test", "prefix") + plg := TestPlugin(t, conn, "test") require.NotNil(plg) assert.NotEmpty(plg.GetPublicId()) } diff --git a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto index 80faeae35..325768684 100644 --- a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto +++ b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto @@ -8,6 +8,7 @@ import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "google/protobuf/struct.proto"; import "controller/api/resources/scopes/v1/scope.proto"; +import "controller/api/resources/plugins/v1/plugin.proto"; import "controller/custom_options/v1/options.proto"; // HostCatalog manages Hosts and Host Sets @@ -21,6 +22,12 @@ message HostCatalog { // Output only. Scope information for this resource. resources.scopes.v1.ScopeInfo scope = 30; + // The ID of the plugin of which this catalog is created. + string plugin_id = 34 [json_name="plugin_id"]; + + // Output only. Plugin information for this resource. + resources.plugins.v1.PluginInfo plugin = 35; + // Optional name for identification purposes. google.protobuf.StringValue name = 40 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; diff --git a/internal/proto/local/controller/api/resources/hosts/v1/host.proto b/internal/proto/local/controller/api/resources/hosts/v1/host.proto index 64ddd494e..1a7794329 100644 --- a/internal/proto/local/controller/api/resources/hosts/v1/host.proto +++ b/internal/proto/local/controller/api/resources/hosts/v1/host.proto @@ -8,6 +8,7 @@ import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "controller/api/resources/scopes/v1/scope.proto"; +import "controller/api/resources/plugins/v1/plugin.proto"; import "controller/custom_options/v1/options.proto"; // Host contains all fields related to a Host resource @@ -21,6 +22,9 @@ message Host { // Output only. Scope information for this resource. resources.scopes.v1.ScopeInfo scope = 30; + // Output only. Plugin information for this resource. + resources.plugins.v1.PluginInfo plugin = 35; + // Optional name for identification purposes. google.protobuf.StringValue name = 40 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index 28f4d204b..2fcdba617 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -8,6 +8,7 @@ import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "controller/api/resources/scopes/v1/scope.proto"; +import "controller/api/resources/plugins/v1/plugin.proto"; import "controller/custom_options/v1/options.proto"; // HostSet is a collection of Hosts created and managed by a Host Catalog @@ -21,6 +22,9 @@ message HostSet { // Output only. Scope information for this resource. resources.scopes.v1.ScopeInfo scope = 30; + // Output only. Plugin information for this resource. + resources.plugins.v1.PluginInfo plugin = 35; + // Optional name for identification purposes. google.protobuf.StringValue name = 40 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; diff --git a/internal/proto/local/controller/api/resources/plugins/v1/plugin.proto b/internal/proto/local/controller/api/resources/plugins/v1/plugin.proto new file mode 100644 index 000000000..2b1e28b38 --- /dev/null +++ b/internal/proto/local/controller/api/resources/plugins/v1/plugin.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package controller.api.resources.plugins.v1; + +option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins;plugins"; + +message PluginInfo { + // Output only. The ID of the Plugin. + string id = 1; + + // Output only. The name of the plugin resource in boundary, if any. + string name = 2; + + // Output only. The description of the plugin in boundary, if any. + string description = 3; +} \ No newline at end of file diff --git a/internal/proto/local/controller/api/services/v1/host_catalog_service.proto b/internal/proto/local/controller/api/services/v1/host_catalog_service.proto index 653f6e98e..ad6fc7dab 100644 --- a/internal/proto/local/controller/api/services/v1/host_catalog_service.proto +++ b/internal/proto/local/controller/api/services/v1/host_catalog_service.proto @@ -103,6 +103,9 @@ message ListHostCatalogsResponse { message CreateHostCatalogRequest { api.resources.hostcatalogs.v1.HostCatalog item = 1; + // As an alternative to providing the plugin id in the provided HostCatalog, + // this field can be used to lookup the plugin using it's name. + string plugin_name = 2; } message CreateHostCatalogResponse { diff --git a/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto b/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto index 75d2efc8e..3d7f54ba2 100644 --- a/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto +++ b/internal/proto/local/controller/storage/plugin/host/store/v1/plugin.proto @@ -34,12 +34,4 @@ message Plugin { // @inject_tag: `gorm:"default:null"` uint32 version = 70; - - // plugin_name is required. The value must be unique across all host plugins - // @inject_tag: `gorm:"default:null"` - string plugin_name = 80; - - // id_prefix is required. The value must be unique across all host plugins - // @inject_tag: `gorm:"default:null"` - string id_prefix = 90; } \ No newline at end of file diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 5772131d1..d5879439c 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -3,7 +3,6 @@ package host_catalogs import ( "context" "fmt" - "strings" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/errors" @@ -13,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/perms" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/common" @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "google.golang.org/grpc/codes" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" @@ -120,7 +121,7 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs return &pbs.ListHostCatalogsResponse{}, nil } - ul, err := s.listFromRepo(ctx, scopeIds) + ul, pluginInfoMap, err := s.listFromRepo(ctx, scopeIds) if err != nil { return nil, err } @@ -160,6 +161,12 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs } outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } + switch hc := item.(type) { + case *plugin.HostCatalog: + if plgInfo, ok := pluginInfoMap[hc.GetPluginId()]; ok { + outputOpts = append(outputOpts, handlers.WithPlugin(plgInfo)) + } + } item, err := toProto(ctx, item, outputOpts...) if err != nil { @@ -184,7 +191,7 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ if authResults.Error != nil { return nil, authResults.Error } - hc, err := s.getFromRepo(ctx, req.GetId()) + hc, plg, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err } @@ -209,6 +216,9 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ } outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } item, err := toProto(ctx, hc, outputOpts...) if err != nil { @@ -229,7 +239,7 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal if authResults.Error != nil { return nil, authResults.Error } - hc, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) + hc, plg, err := s.createInRepo(ctx, authResults.Scope.GetId(), req) if err != nil { return nil, err } @@ -239,7 +249,7 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal return nil, errors.New(ctx, errors.Internal, op, "no request context found") } - outputOpts := make([]handlers.Option, 0, 3) + outputOpts := make([]handlers.Option, 0, 4) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) @@ -254,6 +264,9 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal } outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } item, err := toProto(ctx, hc, outputOpts...) if err != nil { @@ -327,47 +340,49 @@ func (s Service) DeleteHostCatalog(ctx context.Context, req *pbs.DeleteHostCatal return nil, nil } -func (s Service) getFromRepo(ctx context.Context, id string) (host.Catalog, error) { +func (s Service) getFromRepo(ctx context.Context, id string) (host.Catalog, *plugins.PluginInfo, error) { + var plg *plugins.PluginInfo var cat host.Catalog switch host.SubtypeFromId(id) { case static.Subtype: repo, err := s.staticRepoFn() if err != nil { - return nil, err + return nil, nil, err } hc, err := repo.LookupCatalog(ctx, id) if err != nil { - return nil, err + return nil, nil, err } if hc == nil { - return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) + return nil, nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) } cat = hc case plugin.Subtype: repo, err := s.pluginHostRepoFn() if err != nil { - return nil, err + return nil, nil, err } - hc, err := repo.LookupCatalog(ctx, id) + hc, hcplg, err := repo.LookupCatalog(ctx, id) if err != nil { - return nil, err + return nil, nil, err } if hc == nil { - return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) + return nil, nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id) } cat = hc + plg = toPluginInfo(hcplg) } - return cat, nil + return cat, plg, nil } -func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]host.Catalog, error) { +func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]host.Catalog, map[string]*plugins.PluginInfo, error) { repo, err := s.staticRepoFn() if err != nil { - return nil, err + return nil, nil, err } ul, err := repo.ListCatalogs(ctx, scopeIds) if err != nil { - return nil, err + return nil, nil, err } var res []host.Catalog for _, c := range ul { @@ -375,16 +390,21 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]host.Ca } pluginRepo, err := s.pluginHostRepoFn() if err != nil { - return nil, err + return nil, nil, err } - pl, err := pluginRepo.ListCatalogs(ctx, scopeIds) + pl, plgs, err := pluginRepo.ListCatalogs(ctx, scopeIds) if err != nil { - return nil, err + return nil, nil, err } for _, c := range pl { res = append(res, c) } - return res, nil + pluginsMap := make(map[string]*plugins.PluginInfo, len(plgs)) + for _, plg := range plgs { + pluginsMap[plg.GetPublicId()] = toPluginInfo(plg) + } + + return res, pluginsMap, nil } func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) { @@ -407,42 +427,48 @@ func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb return out, nil } -func (s Service) createPluginInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*plugin.HostCatalog, error) { +func (s Service) createPluginInRepo(ctx context.Context, projId string, req *pbs.CreateHostCatalogRequest) (*plugin.HostCatalog, *plugins.PluginInfo, error) { const op = "host_catalogs.(Service).createInRepo" - plgRepo, err := s.pluginRepoFn() - if err != nil { - return nil, errors.Wrap(ctx, err, op) + item := req.GetItem() + pluginId := item.GetPluginId() + if pluginId == "" { + plgRepo, err := s.pluginRepoFn() + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + plg, err := plgRepo.LookupPluginByName(ctx, req.GetPluginName()) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + pluginId = plg.GetPublicId() } - plg, err := plgRepo.LookupPluginByPluginName(ctx, item.GetType()) + h, err := toStoragePluginCatalog(ctx, projId, pluginId, item) if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - h, err := toStoragePluginCatalog(ctx, projId, plg.GetPublicId(), item) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build catalog for creation")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build catalog for creation")) } repo, err := s.pluginHostRepoFn() if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } - out, err := repo.CreateCatalog(ctx, h) + out, plg, err := repo.CreateCatalog(ctx, h) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host catalog")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host catalog")) } if out == nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host catalog but no error returned from repository.") + return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host catalog but no error returned from repository.") } - return out, nil + return out, toPluginInfo(plg), nil } -func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (hc host.Catalog, err error) { - switch host.SubtypeFromType(item.GetType()) { +func (s Service) createInRepo(ctx context.Context, projId string, req *pbs.CreateHostCatalogRequest) (hc host.Catalog, info *plugins.PluginInfo, err error) { + var plg *plugins.PluginInfo + switch host.SubtypeFromType(req.GetItem().GetType()) { case static.Subtype: - hc, err = s.createStaticInRepo(ctx, projId, item) + hc, err = s.createStaticInRepo(ctx, projId, req.GetItem()) default: - hc, err = s.createPluginInRepo(ctx, projId, item) + hc, plg, err = s.createPluginInRepo(ctx, projId, req) } - return hc, err + return hc, plg, err } func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*static.HostCatalog, error) { @@ -531,7 +557,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. res.Error = err return res } - cat, err := repo.LookupCatalog(ctx, id) + cat, _, err := repo.LookupCatalog(ctx, id) if err != nil { res.Error = err return res @@ -548,6 +574,17 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. return auth.Verify(ctx, opts...) } +func toPluginInfo(plg *hostplugin.Plugin) *plugins.PluginInfo { + if plg == nil { + return nil + } + return &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + } +} + func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb.HostCatalog, error) { const op = "host_catalog_service.toProto" opts := handlers.GetOpts(opt...) @@ -563,17 +600,18 @@ func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb. if outputFields.Has(globals.ScopeIdField) { out.ScopeId = in.GetScopeId() } + if outputFields.Has(globals.PluginIdField) { + switch hc := in.(type) { + case *plugin.HostCatalog: + out.PluginId = hc.GetPluginId() + } + } if outputFields.Has(globals.TypeField) { - switch host.SubtypeFromId(in.GetPublicId()) { - case static.Subtype: + switch in.(type) { + case *static.HostCatalog: out.Type = static.Subtype.String() - default: - // TODO: When id prefix and plugin name can diverge, update this logic. - out.Type = "plugin" - idParts := strings.Split(in.GetPublicId(), "_") - if len(idParts) > 2 { - out.Type = idParts[1] - } + case *plugin.HostCatalog: + out.Type = plugin.Subtype.String() } } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { @@ -594,6 +632,9 @@ func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb. if outputFields.Has(globals.ScopeField) { out.Scope = opts.WithScope } + if outputFields.Has(globals.PluginField) { + out.Plugin = opts.WithPlugin + } if outputFields.Has(globals.AuthorizedActionsField) { out.AuthorizedActions = opts.WithAuthorizedActions } @@ -662,13 +703,7 @@ func toStoragePluginCatalog(ctx context.Context, scopeId, plgId string, item *pb // * The type asserted by the ID and/or field is known // * If relevant, the type derived from the id prefix matches what is claimed by the type field func validateGetRequest(req *pbs.GetHostCatalogRequest) error { - pluginPrefix := plugin.HostCatalogPrefix - idParts := strings.Split(req.GetId(), "_") - if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { - pluginPrefix = strings.Join(idParts[:2], "_") - } - - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, pluginPrefix) + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, plugin.HostCatalogPrefix) } func validateCreateRequest(req *pbs.CreateHostCatalogRequest) error { @@ -677,8 +712,20 @@ func validateCreateRequest(req *pbs.CreateHostCatalogRequest) error { if !handlers.ValidId(handlers.Id(req.GetItem().GetScopeId()), scope.Project.Prefix()) { badFields[globals.ScopeIdField] = "This field must be a valid project scope id." } - if req.GetItem().GetType() == "" { - badFields[globals.TypeField] = "This field is required." + switch host.SubtypeFromType(req.GetItem().GetType()) { + case static.Subtype: + case plugin.Subtype: + if req.GetItem().GetPlugin() != nil { + badFields[globals.PluginField] = fmt.Sprintf("This is a read only field.") + } + if req.GetItem().GetPluginId() == "" && req.GetPluginName() == "" { + badFields[globals.PluginIdField] = fmt.Sprintf("This is a required field.") + } + if req.GetItem().GetPluginId() != "" && req.GetPluginName() != "" { + badFields[globals.PluginIdField] = fmt.Sprintf("Can't set the plugin name field along with this field.") + } + default: + badFields[globals.TypeField] = fmt.Sprintf("This is a required field and must be either %q or %q.", static.Subtype.String(), plugin.Subtype.String()) } return badFields }) @@ -690,7 +737,7 @@ func validateUpdateRequest(req *pbs.UpdateHostCatalogRequest) error { switch host.SubtypeFromId(req.GetId()) { case static.Subtype: if req.GetItem().GetType() != "" && host.SubtypeFromType(req.GetItem().GetType()) != static.Subtype { - badFields["type"] = "Cannot modify resource type." + badFields[globals.TypeField] = "Cannot modify resource type." } } return badFields @@ -698,23 +745,17 @@ func validateUpdateRequest(req *pbs.UpdateHostCatalogRequest) error { } func validateDeleteRequest(req *pbs.DeleteHostCatalogRequest) error { - pluginPrefix := plugin.HostCatalogPrefix - idParts := strings.Split(req.GetId(), "_") - if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { - pluginPrefix = strings.Join(idParts[:2], "_") - } - - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, pluginPrefix) + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostCatalogPrefix, plugin.HostCatalogPrefix) } func validateListRequest(req *pbs.ListHostCatalogsRequest) error { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetScopeId()), scope.Project.Prefix()) && !req.GetRecursive() { - badFields["scope_id"] = "This field must be a valid project scope ID or the list operation must be recursive." + badFields[globals.ScopeIdField] = "This field must be a valid project scope ID or the list operation must be recursive." } if _, err := handlers.NewFilter(req.GetFilter()); err != nil { - badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err) + badFields[globals.FilterField] = fmt.Sprintf("This field could not be parsed. %v", err) } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 6f2c64799..797ea630d 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/genproto/protobuf/field_mask" @@ -161,7 +162,7 @@ func TestGet_Plugin(t *testing.T) { return iam.TestRepo(t, conn, wrapper), nil } name := "test" - plg := host.TestPlugin(t, conn, name, name) + plg := host.TestPlugin(t, conn, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) toMerge := &pbs.GetHostCatalogRequest{ @@ -169,12 +170,22 @@ func TestGet_Plugin(t *testing.T) { } pHostCatalog := &pb.HostCatalog{ - Id: hc.GetPublicId(), - ScopeId: hc.GetScopeId(), - Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + Scope: &scopepb.ScopeInfo{ + Id: proj.GetPublicId(), + Type: scope.Project.String(), + ParentScopeId: proj.GetParentId(), + }, + PluginId: plg.GetPublicId(), + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, CreatedTime: hc.CreateTime.GetTimestamp(), UpdatedTime: hc.UpdateTime.GetTimestamp(), - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, AuthorizedCollectionActions: authorizedCollectionActions, } @@ -272,17 +283,23 @@ func TestList(t *testing.T) { var testPluginCatalogs []*pb.HostCatalog name := "test" - plg := host.TestPlugin(t, conn, name, name) + plg := host.TestPlugin(t, conn, name) for i := 0; i < 3; i++ { hc := plugin.TestCatalog(t, conn, pWithCatalogs.GetPublicId(), plg.GetPublicId()) cat := &pb.HostCatalog{ - Id: hc.GetPublicId(), - ScopeId: hc.GetScopeId(), - CreatedTime: hc.GetCreateTime().GetTimestamp(), - UpdatedTime: hc.GetUpdateTime().GetTimestamp(), - Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithCatalogs.GetPublicId()}, + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + CreatedTime: hc.GetCreateTime().GetTimestamp(), + UpdatedTime: hc.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithCatalogs.GetPublicId()}, + PluginId: plg.GetPublicId(), + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Version: 1, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, AuthorizedCollectionActions: authorizedCollectionActions, } @@ -306,17 +323,23 @@ func TestList(t *testing.T) { } name = "different" - plg = host.TestPlugin(t, conn, name, name) + diffPlg := host.TestPlugin(t, conn, name) for i := 0; i < 3; i++ { - hc := plugin.TestCatalog(t, conn, pWithOtherCatalogs.GetPublicId(), plg.GetPublicId()) + hc := plugin.TestCatalog(t, conn, pWithOtherCatalogs.GetPublicId(), diffPlg.GetPublicId()) wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{ - Id: hc.GetPublicId(), - ScopeId: hc.GetScopeId(), - CreatedTime: hc.GetCreateTime().GetTimestamp(), - UpdatedTime: hc.GetUpdateTime().GetTimestamp(), - Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithOtherCatalogs.GetPublicId()}, + Id: hc.GetPublicId(), + ScopeId: hc.GetScopeId(), + CreatedTime: hc.GetCreateTime().GetTimestamp(), + UpdatedTime: hc.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithOtherCatalogs.GetPublicId()}, + PluginId: diffPlg.GetPublicId(), + Plugin: &plugins.PluginInfo{ + Id: diffPlg.GetPublicId(), + Name: diffPlg.GetName(), + Description: diffPlg.GetDescription(), + }, Version: 1, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, AuthorizedCollectionActions: authorizedCollectionActions, }) @@ -369,10 +392,10 @@ func TestList(t *testing.T) { res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs}, }, { - name: "Filter To Catalog Test Type", + name: "Filter To Catalog Using Test Plugin", req: &pbs.ListHostCatalogsRequest{ ScopeId: scope.Global.String(), Recursive: true, - Filter: `"/item/type"=="test"`, + Filter: `"/item/plugin/name"=="test"`, }, res: &pbs.ListHostCatalogsResponse{Items: testPluginCatalogs}, }, @@ -684,7 +707,7 @@ func TestCreate_Plugin(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) - _, proj := iam.TestScopes(t, iamRepo) + org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) repo := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) @@ -697,7 +720,7 @@ func TestCreate_Plugin(t *testing.T) { } name := "test" - plg := host.TestPlugin(t, conn, name, name) + plg := host.TestPlugin(t, conn, name) pluginHostRepo := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{ @@ -723,16 +746,23 @@ func TestCreate_Plugin(t *testing.T) { ScopeId: proj.GetPublicId(), Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, res: &pbs.CreateHostCatalogResponse{ - Uri: fmt.Sprintf("host-catalogs/%s_%s_", plugin.HostCatalogPrefix, name), + Uri: fmt.Sprintf("host-catalogs/%s_", plugin.HostCatalogPrefix), Item: &pb.HostCatalog{ - ScopeId: proj.GetPublicId(), - Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, + ScopeId: proj.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()}, + PluginId: plg.GetPublicId(), + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, AuthorizedCollectionActions: authorizedCollectionActions, }, @@ -741,44 +771,45 @@ func TestCreate_Plugin(t *testing.T) { { name: "Cant create in org", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - ScopeId: proj.GetParentId(), - Name: &wrappers.StringValue{Value: "name"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + ScopeId: org.GetParentId(), + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { name: "Cant create in global", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - ScopeId: scope.Global.String(), - Name: &wrappers.StringValue{Value: "name"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + ScopeId: scope.Global.String(), + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { name: "Create with unknown type", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - Name: &wrappers.StringValue{Value: "name"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: "thisismadeup", + ScopeId: proj.GetPublicId(), + PluginId: plg.GetPublicId(), + Type: "thisismadeup", }}, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { name: "Create with no type", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - Name: &wrappers.StringValue{Value: "name"}, - Description: &wrappers.StringValue{Value: "desc"}, + ScopeId: proj.GetPublicId(), + PluginId: plg.GetPublicId(), }}, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { name: "Can't specify Id", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - Id: "not allowed to be set", + Id: "not allowed to be set", + ScopeId: proj.GetPublicId(), + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), @@ -787,6 +818,9 @@ func TestCreate_Plugin(t *testing.T) { name: "Can't specify Created Time", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ CreatedTime: timestamppb.Now(), + ScopeId: proj.GetPublicId(), + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), @@ -795,6 +829,9 @@ func TestCreate_Plugin(t *testing.T) { name: "Can't specify Update Time", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ UpdatedTime: timestamppb.Now(), + ScopeId: proj.GetPublicId(), + Type: plugin.Subtype.String(), + PluginId: plg.GetPublicId(), }}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), @@ -818,7 +855,7 @@ func TestCreate_Plugin(t *testing.T) { require.NoError(gErr) if got != nil { assert.Contains(got.GetUri(), tc.res.GetUri()) - assert.True(strings.HasPrefix(got.GetItem().GetId(), fmt.Sprintf("%s_%s_", plugin.HostCatalogPrefix, name))) + assert.True(strings.HasPrefix(got.GetItem().GetId(), fmt.Sprintf("%s_", plugin.HostCatalogPrefix))) gotCreateTime := got.GetItem().GetCreatedTime().AsTime() require.NoError(err, "Error converting proto to timestamp.") gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime() diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index f4d8323f1..8cae153bb 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -3,7 +3,6 @@ package host_sets import ( "context" "fmt" - "strings" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/errors" @@ -14,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/perms" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/common" @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/go-secure-stdlib/strutil" "google.golang.org/grpc/codes" "google.golang.org/protobuf/proto" @@ -92,7 +93,7 @@ func (s Service) ListHostSetsWithOptions(ctx context.Context, req *pbs.ListHostS if authResults.Error != nil { return nil, authResults.Error } - hl, err := s.listFromRepo(ctx, req.GetHostCatalogId(), opt...) + hl, plg, err := s.listFromRepo(ctx, req.GetHostCatalogId(), opt...) if err != nil { return nil, err } @@ -127,6 +128,9 @@ func (s Service) ListHostSetsWithOptions(ctx context.Context, req *pbs.ListHostS if outputFields.Has(globals.AuthorizedActionsField) { outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) } + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } item, err := toProto(ctx, item, nil, outputOpts...) if err != nil { @@ -151,7 +155,7 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p if authResults.Error != nil { return nil, authResults.Error } - hs, hosts, err := s.getFromRepo(ctx, req.GetId()) + hs, hosts, plg, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err } @@ -161,7 +165,7 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p return nil, errors.New(ctx, errors.Internal, op, "no request context found") } - outputOpts := make([]handlers.Option, 0, 3) + outputOpts := make([]handlers.Option, 0, 4) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) @@ -169,6 +173,9 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p if outputFields.Has(globals.AuthorizedActionsField) { outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) } + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } item, err := toProto(ctx, hs, hosts, outputOpts...) if err != nil { @@ -189,7 +196,7 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques if authResults.Error != nil { return nil, authResults.Error } - hs, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem().GetHostCatalogId(), req.GetItem()) + hs, plg, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem().GetHostCatalogId(), req.GetItem()) if err != nil { return nil, err } @@ -207,6 +214,9 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques if outputFields.Has(globals.AuthorizedActionsField) { outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) } + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } item, err := toProto(ctx, hs, nil, outputOpts...) if err != nil { @@ -386,21 +396,22 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH return &pbs.RemoveHostSetHostsResponse{Item: item}, nil } -func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.Host, error) { +func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.Host, *plugins.PluginInfo, error) { var hs host.Set var hl []host.Host + var plg *plugins.PluginInfo switch host.SubtypeFromId(id) { case static.Subtype: repo, err := s.staticRepoFn() if err != nil { - return nil, nil, err + return nil, nil, nil, err } hset, hosts, err := repo.LookupSet(ctx, id) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if hset == nil { - return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) + return nil, nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) } for _, h := range hosts { hl = append(hl, h) @@ -409,65 +420,68 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.H case plugin.Subtype: repo, err := s.pluginRepoFn() if err != nil { - return nil, nil, err + return nil, nil, nil, err } - hset, err := repo.LookupSet(ctx, id) + hset, hsplg, err := repo.LookupSet(ctx, id) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if hset == nil { - return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) + return nil, nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id) } hs = hset + plg = toPluginInfo(hsplg) } - return hs, hl, nil + return hs, hl, plg, nil } -func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (host.Set, error) { +func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (host.Set, *plugins.PluginInfo, error) { const op = "host_sets.(Service).createInRepo" if item == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "missing item") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing item") } var hSet host.Set + var plg *plugins.PluginInfo switch host.SubtypeFromId(catalogId) { case static.Subtype: h, err := toStorageStaticSet(ctx, catalogId, item) if err != nil { - return nil, err + return nil, nil, err } repo, err := s.staticRepoFn() if err != nil { - return nil, err + return nil, nil, err } out, err := repo.CreateSet(ctx, scopeId, h) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) } if out == nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") + return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") } hSet = out case plugin.Subtype: h, err := toStoragePluginSet(ctx, catalogId, item) if err != nil { - return nil, err + return nil, nil, err } repo, err := s.pluginRepoFn() if err != nil { - return nil, err + return nil, nil, err } - out, err := repo.CreateSet(ctx, scopeId, h) + out, hsplg, err := repo.CreateSet(ctx, scopeId, h) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set")) } if out == nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") + return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.") } hSet = out + plg = toPluginInfo(hsplg) default: - return nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized catalog type") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized catalog type") } - return hSet, nil + return hSet, plg, nil } func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, error) { @@ -513,18 +527,19 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, return rows > 0, nil } -func (s Service) listFromRepo(ctx context.Context, catalogId string, opt ...host.Option) ([]host.Set, error) { +func (s Service) listFromRepo(ctx context.Context, catalogId string, opt ...host.Option) ([]host.Set, *plugins.PluginInfo, error) { const op = "host_sets.(Service).listFromRepo" + var plg *plugins.PluginInfo var sets []host.Set switch host.SubtypeFromId(catalogId) { case static.Subtype: repo, err := s.staticRepoFn() if err != nil { - return nil, err + return nil, nil, err } sl, err := repo.ListSets(ctx, catalogId) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } for _, a := range sl { sets = append(sets, a) @@ -532,17 +547,18 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string, opt ...host case plugin.Subtype: repo, err := s.pluginRepoFn() if err != nil { - return nil, err + return nil, nil, err } - sl, err := repo.ListSets(ctx, catalogId, opt...) + sl, hsplg, err := repo.ListSets(ctx, catalogId, opt...) if err != nil { - return nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } for _, a := range sl { sets = append(sets, a) } + plg = toPluginInfo(hsplg) } - return sets, nil + return sets, plg, nil } func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) { @@ -652,7 +668,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty } set = ss case plugin.Subtype: - ps, err := pluginRepo.LookupSet(ctx, id) + ps, _, err := pluginRepo.LookupSet(ctx, id) if err != nil { res.Error = err return nil, res @@ -681,7 +697,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty } cat = cs case plugin.Subtype: - pc, err := pluginRepo.LookupCatalog(ctx, parentId) + pc, _, err := pluginRepo.LookupCatalog(ctx, parentId) if err != nil { res.Error = err return nil, res @@ -696,6 +712,17 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty return cat, auth.Verify(ctx, opts...) } +func toPluginInfo(plg *hostplugin.Plugin) *plugins.PluginInfo { + if plg == nil { + return nil + } + return &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + } +} + func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handlers.Option) (*pb.HostSet, error) { const op = "host_set_service.toProto" opts := handlers.GetOpts(opt...) @@ -712,15 +739,11 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler out.HostCatalogId = in.GetCatalogId() } if outputFields.Has(globals.TypeField) { - switch host.SubtypeFromId(in.GetPublicId()) { - case static.Subtype: + switch in.(type) { + case *static.HostSet: out.Type = static.Subtype.String() - case plugin.Subtype: - out.Type = "plugin" - idParts := strings.Split(in.GetPublicId(), "_") - if len(idParts) > 2 { - out.Type = idParts[1] - } + case *plugin.HostSet: + out.Type = plugin.Subtype.String() } } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { @@ -741,6 +764,9 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler if outputFields.Has(globals.ScopeField) { out.Scope = opts.WithScope } + if outputFields.Has(globals.PluginField) { + out.Plugin = opts.WithPlugin + } if outputFields.Has(globals.AuthorizedActionsField) { out.AuthorizedActions = opts.WithAuthorizedActions } @@ -816,26 +842,14 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) // * The type asserted by the ID and/or field is known // * If relevant, the type derived from the id prefix matches what is claimed by the type field func validateGetRequest(req *pbs.GetHostSetRequest) error { - pluginPrefix := plugin.HostSetPrefix - idParts := strings.Split(req.GetId(), "_") - if len(idParts) > 2 && idParts[0] == plugin.HostSetPrefix { - pluginPrefix = strings.Join(idParts[:2], "_") - } - - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, pluginPrefix) + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, plugin.HostSetPrefix) } func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) error { return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { badFields := map[string]string{} - pluginPrefix := plugin.HostCatalogPrefix - idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_") - if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { - pluginPrefix = strings.Join(idParts[:2], "_") - } - - if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) { - badFields["host_catalog_id"] = "The field is incorrectly formatted." + if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix, plugin.HostCatalogPrefix) { + badFields[globals.HostCatalogIdField] = "The field is incorrectly formatted." } if len(req.GetItem().GetPreferredEndpoints()) > 0 { _, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(req.GetItem().GetPreferredEndpoints())) @@ -846,21 +860,11 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) e switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) { case static.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { - badFields["type"] = "Doesn't match the parent resource's type." + badFields[globals.TypeField] = "Doesn't match the parent resource's type." } case plugin.Subtype: - // TODO: Remove this check when plugin id prefixes can differ from plugin name - if req.GetItem().GetType() == "" { - break - } - idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_") - if len(idParts) < 2 { - badFields["host_catalog_id"] = "The field is incorrectly formatted for a plugin id." - break - } - plgType := idParts[1] - if plgType != req.GetItem().GetType() { - badFields["type"] = "This type must match the type of the host catalog." + if req.GetItem().GetType() != "" && req.GetItem().GetType() != plugin.Subtype.String() { + badFields[globals.TypeField] = "Doesn't match the parent resource's type." } } return badFields @@ -891,18 +895,12 @@ func validateDeleteRequest(req *pbs.DeleteHostSetRequest) error { } func validateListRequest(req *pbs.ListHostSetsRequest) error { - pluginPrefix := plugin.HostCatalogPrefix - idParts := strings.Split(req.GetHostCatalogId(), "_") - if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix { - pluginPrefix = strings.Join(idParts[:2], "_") - } - badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) { - badFields["host_catalog_id"] = "The field is incorrectly formatted." + if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, plugin.HostCatalogPrefix) { + badFields[globals.HostCatalogIdField] = "The field is incorrectly formatted." } if _, err := handlers.NewFilter(req.GetFilter()); err != nil { - badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err) + badFields[globals.FilterField] = fmt.Sprintf("This field could not be parsed. %v", err) } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields) @@ -913,17 +911,17 @@ func validateListRequest(req *pbs.ListHostSetsRequest) error { func validateAddRequest(req *pbs.AddHostSetHostsRequest) error { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetId()), static.HostSetPrefix) { - badFields["id"] = "Incorrectly formatted identifier." + badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { - badFields["version"] = "Required field." + badFields[globals.VersionField] = "Required field." } if len(req.GetHostIds()) == 0 { - badFields["host_ids"] = "Must be non-empty." + badFields[globals.HostIdsField] = "Must be non-empty." } for _, id := range req.GetHostIds() { if !handlers.ValidId(handlers.Id(id), static.HostPrefix) { - badFields["host_ids"] = "Incorrectly formatted host identifier." + badFields[globals.HostIdsField] = "Incorrectly formatted host identifier." break } } @@ -936,14 +934,14 @@ func validateAddRequest(req *pbs.AddHostSetHostsRequest) error { func validateSetRequest(req *pbs.SetHostSetHostsRequest) error { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetId()), static.HostSetPrefix) { - badFields["id"] = "Incorrectly formatted identifier." + badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { - badFields["version"] = "Required field." + badFields[globals.VersionField] = "Required field." } for _, id := range req.GetHostIds() { if !handlers.ValidId(handlers.Id(id), static.HostPrefix) { - badFields["host_ids"] = "Incorrectly formatted host identifier." + badFields[globals.HostIdsField] = "Incorrectly formatted host identifier." break } } @@ -956,17 +954,17 @@ func validateSetRequest(req *pbs.SetHostSetHostsRequest) error { func validateRemoveRequest(req *pbs.RemoveHostSetHostsRequest) error { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetId()), static.HostSetPrefix) { - badFields["id"] = "Incorrectly formatted identifier." + badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { - badFields["version"] = "Required field." + badFields[globals.VersionField] = "Required field." } if len(req.GetHostIds()) == 0 { - badFields["host_ids"] = "Must be non-empty." + badFields[globals.HostIdsField] = "Must be non-empty." } for _, id := range req.GetHostIds() { if !handlers.ValidId(handlers.Id(id), static.HostPrefix) { - badFields["host_ids"] = "Incorrectly formatted host identifier." + badFields[globals.HostIdsField] = "Incorrectly formatted host identifier." break } } diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 09ab45673..a772e29cc 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" @@ -153,7 +154,7 @@ func TestGet_Plugin(t *testing.T) { name := "test" prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} - plg := hostplugin.TestPlugin(t, conn, name, name) + plg := hostplugin.TestPlugin(t, conn, name) hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) hs := plugin.TestSet(t, conn, kms, hc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -162,12 +163,17 @@ func TestGet_Plugin(t *testing.T) { toMerge := &pbs.GetHostSetRequest{} pHost := &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Id: hs.GetPublicId(), - CreatedTime: hs.CreateTime.GetTimestamp(), - UpdatedTime: hs.UpdateTime.GetTimestamp(), - Type: name, - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + HostCatalogId: hc.GetPublicId(), + Id: hs.GetPublicId(), + CreatedTime: hs.CreateTime.GetTimestamp(), + UpdatedTime: hs.UpdateTime.GetTimestamp(), + Type: plugin.Subtype.String(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, PreferredEndpoints: prefEndpoints, AuthorizedActions: testAuthorizedActions, } @@ -342,7 +348,7 @@ func TestList_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } name := "test" - plg := hostplugin.TestPlugin(t, conn, name, name) + plg := hostplugin.TestPlugin(t, conn, name) plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, } @@ -357,13 +363,18 @@ func TestList_Plugin(t *testing.T) { for i := 0; i < 10; i++ { h := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) wantHs = append(wantHs, &pb.HostSet{ - Id: h.GetPublicId(), - HostCatalogId: h.GetCatalogId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Id: h.GetPublicId(), + HostCatalogId: h.GetCatalogId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, CreatedTime: h.GetCreateTime().GetTimestamp(), UpdatedTime: h.GetUpdateTime().GetTimestamp(), Version: h.GetVersion(), - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, PreferredEndpoints: preferredEndpoints, }) @@ -707,7 +718,7 @@ func TestCreate_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } name := "test" - plg := hostplugin.TestPlugin(t, conn, name, name) + plg := hostplugin.TestPlugin(t, conn, name) plgRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{ @@ -757,16 +768,21 @@ func TestCreate_Plugin(t *testing.T) { HostCatalogId: hc.GetPublicId(), Name: &wrappers.StringValue{Value: "No Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), }}, res: &pbs.CreateHostSetResponse{ - Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Name: &wrappers.StringValue{Value: "No Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, }, }, @@ -777,17 +793,22 @@ func TestCreate_Plugin(t *testing.T) { HostCatalogId: hc.GetPublicId(), Name: &wrappers.StringValue{Value: "With Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), Attributes: testAttrs, }}, res: &pbs.CreateHostSetResponse{ - Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Name: &wrappers.StringValue{Value: "With Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, Attributes: testAttrs, }, @@ -799,17 +820,22 @@ func TestCreate_Plugin(t *testing.T) { HostCatalogId: hc.GetPublicId(), Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), PreferredEndpoints: prefEndpoints, }}, res: &pbs.CreateHostSetResponse{ - Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), PreferredEndpoints: prefEndpoints, AuthorizedActions: testAuthorizedActions, }, @@ -821,7 +847,7 @@ func TestCreate_Plugin(t *testing.T) { HostCatalogId: hc.GetPublicId(), Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, - Type: name, + Type: plugin.Subtype.String(), PreferredEndpoints: append(prefEndpoints, "foobar:1.2.3.4"), }}, err: handlers.ApiErrorWithCode(codes.InvalidArgument), @@ -844,13 +870,18 @@ func TestCreate_Plugin(t *testing.T) { Description: &wrappers.StringValue{Value: "no type desc"}, }}, res: &pbs.CreateHostSetResponse{ - Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name), + Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + HostCatalogId: hc.GetPublicId(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, Name: &wrappers.StringValue{Value: "no type name"}, Description: &wrappers.StringValue{Value: "no type desc"}, - Type: name, + Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, }, }, diff --git a/internal/servers/controller/handlers/options.go b/internal/servers/controller/handlers/options.go index 65414c2ca..c41c735df 100644 --- a/internal/servers/controller/handlers/options.go +++ b/internal/servers/controller/handlers/options.go @@ -2,6 +2,7 @@ package handlers import ( "github.com/hashicorp/boundary/internal/perms" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" "google.golang.org/protobuf/types/known/structpb" ) @@ -26,6 +27,7 @@ type options struct { WithUserIsAnonymous bool WithOutputFields *perms.OutputFieldsMap WithScope *scopes.ScopeInfo + WithPlugin *plugins.PluginInfo WithAuthorizedActions []string WithAuthorizedCollectionActions map[string]*structpb.ListValue WithManagedGroupIds []string @@ -69,6 +71,14 @@ func WithScope(scp *scopes.ScopeInfo) Option { } } +// WithPlugin provides an option when creating responses to include the given +// plugin if allowed +func WithPlugin(plg *plugins.PluginInfo) Option { + return func(o *options) { + o.WithPlugin = plg + } +} + // WithAuthorizedActions provides an option when creating responses to include the given // authorized actions if allowed func WithAuthorizedActions(acts []string) Option { diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 98da7aab1..cedd2b038 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -982,7 +982,7 @@ func TestAddTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -1116,7 +1116,7 @@ func TestSetTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -1244,7 +1244,7 @@ func TestRemoveTargetHostSets(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -1384,7 +1384,7 @@ func TestAddTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -1518,7 +1518,7 @@ func TestSetTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, @@ -1640,7 +1640,7 @@ func TestRemoveTargetHostSources(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - plg := host.TestPlugin(t, conn, "test", "test") + plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, diff --git a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go index f72352587..988b3ccb9 100644 --- a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go +++ b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go @@ -7,6 +7,7 @@ package hostcatalogs import ( + plugins "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -37,6 +38,10 @@ type HostCatalog struct { ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` + // The ID of the plugin of which this catalog is created. + PluginId string `protobuf:"bytes,34,opt,name=plugin_id,proto3" json:"plugin_id,omitempty"` + // Output only. Plugin information for this resource. + Plugin *plugins.PluginInfo `protobuf:"bytes,35,opt,name=plugin,proto3" json:"plugin,omitempty"` // Optional name for identification purposes. Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty"` // Optional user-set description for identification purposes. @@ -113,6 +118,20 @@ func (x *HostCatalog) GetScope() *scopes.ScopeInfo { return nil } +func (x *HostCatalog) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *HostCatalog) GetPlugin() *plugins.PluginInfo { + if x != nil { + return x.Plugin + } + return nil +} + func (x *HostCatalog) GetName() *wrapperspb.StringValue { if x != nil { return x.Name @@ -201,73 +220,82 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_rawDesc = [ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x8d, 0x07, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, - 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, - 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, - 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x56, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6a, 0x0a, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf4, 0x07, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, + 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x23, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, + 0x18, 0x6e, 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, 0x42, + 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2f, + 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x9d, 0x01, 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, + 0x6a, 0x0a, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, + 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -287,26 +315,28 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_goTypes = [ (*HostCatalog)(nil), // 0: controller.api.resources.hostcatalogs.v1.HostCatalog nil, // 1: controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry (*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 5: google.protobuf.Struct - (*structpb.ListValue)(nil), // 6: google.protobuf.ListValue + (*plugins.PluginInfo)(nil), // 3: controller.api.resources.plugins.v1.PluginInfo + (*wrapperspb.StringValue)(nil), // 4: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 6: google.protobuf.Struct + (*structpb.ListValue)(nil), // 7: google.protobuf.ListValue } var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_depIdxs = []int32{ - 2, // 0: controller.api.resources.hostcatalogs.v1.HostCatalog.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 3, // 1: controller.api.resources.hostcatalogs.v1.HostCatalog.name:type_name -> google.protobuf.StringValue - 3, // 2: controller.api.resources.hostcatalogs.v1.HostCatalog.description:type_name -> google.protobuf.StringValue - 4, // 3: controller.api.resources.hostcatalogs.v1.HostCatalog.created_time:type_name -> google.protobuf.Timestamp - 4, // 4: controller.api.resources.hostcatalogs.v1.HostCatalog.updated_time:type_name -> google.protobuf.Timestamp - 5, // 5: controller.api.resources.hostcatalogs.v1.HostCatalog.attributes:type_name -> google.protobuf.Struct - 5, // 6: controller.api.resources.hostcatalogs.v1.HostCatalog.secrets:type_name -> google.protobuf.Struct - 1, // 7: controller.api.resources.hostcatalogs.v1.HostCatalog.authorized_collection_actions:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry - 6, // 8: controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry.value:type_name -> google.protobuf.ListValue - 9, // [9:9] is the sub-list for method output_type - 9, // [9:9] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 2, // 0: controller.api.resources.hostcatalogs.v1.HostCatalog.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 3, // 1: controller.api.resources.hostcatalogs.v1.HostCatalog.plugin:type_name -> controller.api.resources.plugins.v1.PluginInfo + 4, // 2: controller.api.resources.hostcatalogs.v1.HostCatalog.name:type_name -> google.protobuf.StringValue + 4, // 3: controller.api.resources.hostcatalogs.v1.HostCatalog.description:type_name -> google.protobuf.StringValue + 5, // 4: controller.api.resources.hostcatalogs.v1.HostCatalog.created_time:type_name -> google.protobuf.Timestamp + 5, // 5: controller.api.resources.hostcatalogs.v1.HostCatalog.updated_time:type_name -> google.protobuf.Timestamp + 6, // 6: controller.api.resources.hostcatalogs.v1.HostCatalog.attributes:type_name -> google.protobuf.Struct + 6, // 7: controller.api.resources.hostcatalogs.v1.HostCatalog.secrets:type_name -> google.protobuf.Struct + 1, // 8: controller.api.resources.hostcatalogs.v1.HostCatalog.authorized_collection_actions:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry + 7, // 9: controller.api.resources.hostcatalogs.v1.HostCatalog.AuthorizedCollectionActionsEntry.value:type_name -> google.protobuf.ListValue + 10, // [10:10] is the sub-list for method output_type + 10, // [10:10] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_init() } diff --git a/sdk/pbs/controller/api/resources/hosts/host.pb.go b/sdk/pbs/controller/api/resources/hosts/host.pb.go index cca080e1e..f568bde37 100644 --- a/sdk/pbs/controller/api/resources/hosts/host.pb.go +++ b/sdk/pbs/controller/api/resources/hosts/host.pb.go @@ -7,6 +7,7 @@ package hosts import ( + plugins "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -37,6 +38,8 @@ type Host struct { HostCatalogId string `protobuf:"bytes,20,opt,name=host_catalog_id,proto3" json:"host_catalog_id,omitempty"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` + // Output only. Plugin information for this resource. + Plugin *plugins.PluginInfo `protobuf:"bytes,35,opt,name=plugin,proto3" json:"plugin,omitempty"` // Optional name for identification purposes. Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty"` // Optional user-set description for identification purposes. @@ -111,6 +114,13 @@ func (x *Host) GetScope() *scopes.ScopeInfo { return nil } +func (x *Host) GetPlugin() *plugins.PluginInfo { + if x != nil { + return x.Plugin + } + return nil +} + func (x *Host) GetName() *wrapperspb.StringValue { if x != nil { return x.Name @@ -239,61 +249,69 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{ 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x04, 0x0a, - 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x12, - 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x22, - 0x0a, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, - 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x18, 0x6e, 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, 0x42, - 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, + 0x1a, 0x30, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, + 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbc, + 0x05, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, + 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, + 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, + 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x68, 0x6f, 0x73, + 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x3d, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x75, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, - 0xdd, 0x29, 0x1d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, - 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75, 0x0a, + 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x12, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, + 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -313,23 +331,25 @@ var file_controller_api_resources_hosts_v1_host_proto_goTypes = []interface{}{ (*Host)(nil), // 0: controller.api.resources.hosts.v1.Host (*StaticHostAttributes)(nil), // 1: controller.api.resources.hosts.v1.StaticHostAttributes (*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 5: google.protobuf.Struct + (*plugins.PluginInfo)(nil), // 3: controller.api.resources.plugins.v1.PluginInfo + (*wrapperspb.StringValue)(nil), // 4: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 6: google.protobuf.Struct } var file_controller_api_resources_hosts_v1_host_proto_depIdxs = []int32{ 2, // 0: controller.api.resources.hosts.v1.Host.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 3, // 1: controller.api.resources.hosts.v1.Host.name:type_name -> google.protobuf.StringValue - 3, // 2: controller.api.resources.hosts.v1.Host.description:type_name -> google.protobuf.StringValue - 4, // 3: controller.api.resources.hosts.v1.Host.created_time:type_name -> google.protobuf.Timestamp - 4, // 4: controller.api.resources.hosts.v1.Host.updated_time:type_name -> google.protobuf.Timestamp - 5, // 5: controller.api.resources.hosts.v1.Host.attributes:type_name -> google.protobuf.Struct - 3, // 6: controller.api.resources.hosts.v1.StaticHostAttributes.address:type_name -> google.protobuf.StringValue - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 3, // 1: controller.api.resources.hosts.v1.Host.plugin:type_name -> controller.api.resources.plugins.v1.PluginInfo + 4, // 2: controller.api.resources.hosts.v1.Host.name:type_name -> google.protobuf.StringValue + 4, // 3: controller.api.resources.hosts.v1.Host.description:type_name -> google.protobuf.StringValue + 5, // 4: controller.api.resources.hosts.v1.Host.created_time:type_name -> google.protobuf.Timestamp + 5, // 5: controller.api.resources.hosts.v1.Host.updated_time:type_name -> google.protobuf.Timestamp + 6, // 6: controller.api.resources.hosts.v1.Host.attributes:type_name -> google.protobuf.Struct + 4, // 7: controller.api.resources.hosts.v1.StaticHostAttributes.address:type_name -> google.protobuf.StringValue + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_controller_api_resources_hosts_v1_host_proto_init() } diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index 153dbdf1f..787136971 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -7,6 +7,7 @@ package hostsets import ( + plugins "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -37,6 +38,8 @@ type HostSet struct { HostCatalogId string `protobuf:"bytes,20,opt,name=host_catalog_id,proto3" json:"host_catalog_id,omitempty"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` + // Output only. Plugin information for this resource. + Plugin *plugins.PluginInfo `protobuf:"bytes,35,opt,name=plugin,proto3" json:"plugin,omitempty"` // Optional name for identification purposes. Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty"` // Optional user-set description for identification purposes. @@ -114,6 +117,13 @@ func (x *HostSet) GetScope() *scopes.ScopeInfo { return nil } +func (x *HostSet) GetPlugin() *plugins.PluginInfo { + if x != nil { + return x.Plugin + } + return nil +} + func (x *HostSet) GetName() *wrapperspb.StringValue { if x != nil { return x.Name @@ -201,58 +211,65 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, - 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, - 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe9, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, + 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, + 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x12, 0x43, + 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x23, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, - 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, - 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x68, - 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, - 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x0a, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, - 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x09, 0x42, + 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, + 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -271,22 +288,24 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_msgTypes = make([]p var file_controller_api_resources_hostsets_v1_host_set_proto_goTypes = []interface{}{ (*HostSet)(nil), // 0: controller.api.resources.hostsets.v1.HostSet (*scopes.ScopeInfo)(nil), // 1: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 2: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 4: google.protobuf.Struct + (*plugins.PluginInfo)(nil), // 2: controller.api.resources.plugins.v1.PluginInfo + (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 5: google.protobuf.Struct } var file_controller_api_resources_hostsets_v1_host_set_proto_depIdxs = []int32{ 1, // 0: controller.api.resources.hostsets.v1.HostSet.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 2, // 1: controller.api.resources.hostsets.v1.HostSet.name:type_name -> google.protobuf.StringValue - 2, // 2: controller.api.resources.hostsets.v1.HostSet.description:type_name -> google.protobuf.StringValue - 3, // 3: controller.api.resources.hostsets.v1.HostSet.created_time:type_name -> google.protobuf.Timestamp - 3, // 4: controller.api.resources.hostsets.v1.HostSet.updated_time:type_name -> google.protobuf.Timestamp - 4, // 5: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 2, // 1: controller.api.resources.hostsets.v1.HostSet.plugin:type_name -> controller.api.resources.plugins.v1.PluginInfo + 3, // 2: controller.api.resources.hostsets.v1.HostSet.name:type_name -> google.protobuf.StringValue + 3, // 3: controller.api.resources.hostsets.v1.HostSet.description:type_name -> google.protobuf.StringValue + 4, // 4: controller.api.resources.hostsets.v1.HostSet.created_time:type_name -> google.protobuf.Timestamp + 4, // 5: controller.api.resources.hostsets.v1.HostSet.updated_time:type_name -> google.protobuf.Timestamp + 5, // 6: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_controller_api_resources_hostsets_v1_host_set_proto_init() } diff --git a/sdk/pbs/controller/api/resources/plugins/plugin.pb.go b/sdk/pbs/controller/api/resources/plugins/plugin.pb.go new file mode 100644 index 000000000..b670e6a36 --- /dev/null +++ b/sdk/pbs/controller/api/resources/plugins/plugin.pb.go @@ -0,0 +1,172 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: controller/api/resources/plugins/v1/plugin.proto + +package plugins + +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) +) + +type PluginInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Output only. The ID of the Plugin. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Output only. The name of the plugin resource in boundary, if any. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Output only. The description of the plugin in boundary, if any. + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *PluginInfo) Reset() { + *x = PluginInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_plugins_v1_plugin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PluginInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PluginInfo) ProtoMessage() {} + +func (x *PluginInfo) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_plugins_v1_plugin_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 PluginInfo.ProtoReflect.Descriptor instead. +func (*PluginInfo) Descriptor() ([]byte, []int) { + return file_controller_api_resources_plugins_v1_plugin_proto_rawDescGZIP(), []int{0} +} + +func (x *PluginInfo) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *PluginInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PluginInfo) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +var File_controller_api_resources_plugins_v1_plugin_proto protoreflect.FileDescriptor + +var file_controller_api_resources_plugins_v1_plugin_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x52, 0x0a, 0x0a, 0x50, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x50, 0x5a, 0x4e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x73, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_resources_plugins_v1_plugin_proto_rawDescOnce sync.Once + file_controller_api_resources_plugins_v1_plugin_proto_rawDescData = file_controller_api_resources_plugins_v1_plugin_proto_rawDesc +) + +func file_controller_api_resources_plugins_v1_plugin_proto_rawDescGZIP() []byte { + file_controller_api_resources_plugins_v1_plugin_proto_rawDescOnce.Do(func() { + file_controller_api_resources_plugins_v1_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_resources_plugins_v1_plugin_proto_rawDescData) + }) + return file_controller_api_resources_plugins_v1_plugin_proto_rawDescData +} + +var file_controller_api_resources_plugins_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_api_resources_plugins_v1_plugin_proto_goTypes = []interface{}{ + (*PluginInfo)(nil), // 0: controller.api.resources.plugins.v1.PluginInfo +} +var file_controller_api_resources_plugins_v1_plugin_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_api_resources_plugins_v1_plugin_proto_init() } +func file_controller_api_resources_plugins_v1_plugin_proto_init() { + if File_controller_api_resources_plugins_v1_plugin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_resources_plugins_v1_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PluginInfo); 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_controller_api_resources_plugins_v1_plugin_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_api_resources_plugins_v1_plugin_proto_goTypes, + DependencyIndexes: file_controller_api_resources_plugins_v1_plugin_proto_depIdxs, + MessageInfos: file_controller_api_resources_plugins_v1_plugin_proto_msgTypes, + }.Build() + File_controller_api_resources_plugins_v1_plugin_proto = out.File + file_controller_api_resources_plugins_v1_plugin_proto_rawDesc = nil + file_controller_api_resources_plugins_v1_plugin_proto_goTypes = nil + file_controller_api_resources_plugins_v1_plugin_proto_depIdxs = nil +} From 8b8f5de1206d0a2bde81226247a8b0832adb51ba Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Fri, 24 Sep 2021 13:55:19 -0700 Subject: [PATCH 22/71] sdk: update grpc (#1538) --- sdk/go.mod | 1 + sdk/go.sum | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sdk/go.mod b/sdk/go.mod index 46a26b3d1..0bd8bbd26 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -7,5 +7,6 @@ require ( github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 github.com/stretchr/testify v1.7.0 + google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 ) diff --git a/sdk/go.sum b/sdk/go.sum index 2d95fcea9..57823026c 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -65,6 +65,7 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -72,6 +73,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f h1:oRD16bhpKNAanfcDDVU+J0NXqsgHIvGbbe/sy+r6Rs0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -90,6 +92,7 @@ github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9cop github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -98,6 +101,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -125,6 +130,8 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -175,6 +182,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= @@ -205,6 +213,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -379,6 +388,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -392,6 +402,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -417,6 +428,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -427,6 +439,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -482,8 +495,9 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -577,8 +591,9 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -616,6 +631,7 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -630,8 +646,11 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -661,6 +680,7 @@ gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 14cc97b645a15703069645fffba7b1e91c046a00 Mon Sep 17 00:00:00 2001 From: Todd Date: Sun, 26 Sep 2021 08:17:21 -0700 Subject: [PATCH 23/71] Add authorize session for plugin based host sets. (#1559) Added a new Endpoint struct at the top level host package level to capture host, set, and address chosen. Temporarily add a host_plugin_host table to capture the hosts returned by the plugins. This ensures the datawarehouse is able to query the table to decide what host was chosen. The view used to update the datawarehouse dimension table provides rows with host set ids and hosts which are not in that host set. This is ok since the function which updates the dimension table provides the host set and host id chosen so that relationship doesn't need to be captured in the view itself. Co-authored-by: Jeff Mitchell --- .../postgres/17/04_plugin_host.up.sql | 60 ++++ .../17/06_wh_session_dimensions.up.sql | 85 +++++ internal/db/schema/postgres_migration.gen.go | 145 ++++++++- internal/host/endpoint.go | 9 + internal/host/host.go | 23 ++ internal/host/plugin/host.go | 76 +++++ internal/host/plugin/host_test.go | 202 ++++++++++++ internal/host/plugin/ids.go | 17 +- .../host/plugin/repository_host_catalog.go | 30 +- internal/host/plugin/repository_host_set.go | 293 ++++++++++++++--- .../host/plugin/repository_host_set_test.go | 134 ++++++++ internal/host/plugin/store/host.pb.go | 298 +++++++++++++----- internal/host/static/repository_host_set.go | 55 ++-- internal/host/subtype.go | 20 -- .../storage/host/plugin/store/v1/host.proto | 36 +++ internal/servers/controller/handler.go | 2 + .../handlers/targets/target_service.go | 121 +++---- .../handlers/targets/target_service_test.go | 262 +++++++++------ 18 files changed, 1536 insertions(+), 332 deletions(-) create mode 100644 internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql create mode 100644 internal/host/endpoint.go create mode 100644 internal/host/host.go create mode 100644 internal/host/plugin/host.go create mode 100644 internal/host/plugin/host_test.go diff --git a/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql b/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql index d3b80e48c..1ca7386f2 100644 --- a/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql +++ b/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql @@ -154,6 +154,66 @@ begin; create trigger delete_host_set_subtype after delete on host_plugin_set for each row execute procedure delete_host_set_subtype(); + + -- TODO: Rebuild this table to appropriately cache or sync data from the + -- plugin'ed service. + -- host_plugin_host captures plugin based host data. This is only written to + -- from the controller and is not mutable directly by actions from the end + -- user. + create table host_plugin_host ( + public_id wt_public_id primary key, + catalog_id wt_public_id not null + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + + -- TODO: Break out the address to match the domain model where a host + -- can have multiple addresses and relies on the set to select the + -- prefered one. + address text not null + constraint address_must_be_more_than_2_characters + check(length(trim(address)) > 2) + constraint address_must_be_less_than_256_characters + check(length(trim(address)) < 256), + + constraint host_fkey + foreign key (catalog_id, public_id) + references host (catalog_id, public_id) + on delete cascade + on update cascade, + + constraint host_plugin_host_catalog_id_name_uq + unique(catalog_id, name), + + constraint host_plugin_host_catalog_id_public_id_uq + unique(catalog_id, public_id) + ); + + create trigger update_version_column after update on host_plugin_host + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_host + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_host + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_host + for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + + create trigger insert_host_subtype before insert on host_plugin_host + for each row execute procedure insert_host_subtype(); + + create trigger delete_host_subtype after delete on host_plugin_host + for each row execute procedure delete_host_subtype(); + + insert into oplog_ticket (name, version) values ('host_plugin_catalog', 1), diff --git a/internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql b/internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql new file mode 100644 index 000000000..6c8b9daac --- /dev/null +++ b/internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql @@ -0,0 +1,85 @@ +begin; + +-- replaces view from 15/02_wh_rename_host_dimension_org.up.sql + +-- Updates whx_host_dimension_source to support plugin based host and related +-- resources. While this will return rows where there are host_sets where hosts +-- are not in the host sets, this view is only used when looking up a row by +-- host, set, and target so the records where hosts and host sets aren't +-- related beyond the shared catalog will be ignored. +-- TODO: Support a host having multiple addresses. +drop view whx_host_dimension_source; +create view whx_host_dimension_source as +select -- id is the first column in the target view + h.public_id as host_id, + case when sh.public_id is not null then 'static host' + when ph.public_id is not null then 'plugin host' + else 'Unknown' end as host_type, + case when sh.public_id is not null then coalesce(sh.name, 'None') + when ph.public_id is not null then coalesce(ph.name, 'None') + else 'Unknown' end as host_name, + case when sh.public_id is not null then coalesce(sh.description, 'None') + when ph.public_id is not null then coalesce(ph.description, 'None') + else 'Unknown' end as host_description, + + -- TODO: update this to be endpoint address... + coalesce(sh.address, 'Unknown') as host_address, + + hs.public_id as host_set_id, + case when shs.public_id is not null then 'static host set' + when phs.public_id is not null then 'plugin host set' + else 'Unknown' end as host_set_type, + case + when shs.public_id is not null then coalesce(shs.name, 'None') + when phs.public_id is not null then coalesce(phs.name, 'None') + else 'None' + end as host_set_name, + case + when shs.public_id is not null then coalesce(shs.description, 'None') + when phs.public_id is not null then coalesce(phs.description, 'None') + else 'None' + end as host_set_description, + hc.public_id as host_catalog_id, + case when shc.public_id is not null then 'static host catalog' + when phc.public_id is not null then 'plugin host catalog' + else 'Unknown' end as host_catalog_type, + case + when shc.public_id is not null then coalesce(shc.name, 'None') + when phc.public_id is not null then coalesce(phc.name, 'None') + else 'None' + end as host_catalog_name, + case + when shc.public_id is not null then coalesce(shc.description, 'None') + when phc.public_id is not null then coalesce(phc.description, 'None') + else 'None' + end as host_catalog_description, + t.public_id as target_id, + 'tcp target' as target_type, + coalesce(t.name, 'None') as target_name, + coalesce(t.description, 'None') as target_description, + coalesce(t.default_port, 0) as target_default_port_number, + t.session_max_seconds as target_session_max_seconds, + t.session_connection_limit as target_session_connection_limit, + p.public_id as project_id, + coalesce(p.name, 'None') as project_name, + coalesce(p.description, 'None') as project_description, + o.public_id as organization_id, + coalesce(o.name, 'None') as organization_name, + coalesce(o.description, 'None') as organization_description + from host as h + join host_catalog as hc on h.catalog_id = hc.public_id + join host_set as hs on h.catalog_id = hs.catalog_id + join target_host_set as ts on hs.public_id = ts.host_set_id + join target_tcp as t on ts.target_id = t.public_id + join iam_scope as p on t.scope_id = p.public_id and p.type = 'project' + join iam_scope as o on p.parent_id = o.public_id and o.type = 'org' + + left join static_host as sh on sh.public_id = h.public_id + left join host_plugin_host as ph on ph.public_id = h.public_id + left join static_host_catalog as shc on shc.public_id = hc.public_id + left join host_plugin_catalog as phc on phc.public_id = hc.public_id + left join static_host_set as shs on shs.public_id = hs.public_id + left join host_plugin_set as phs on phs.public_id = hs.public_id +; + +commit; diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 5504c5ef4..f8f42984c 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -4,7 +4,7 @@ package schema func init() { migrationStates["postgres"] = migrationState{ - binarySchemaVersion: 17005, + binarySchemaVersion: 17006, upMigrations: map[int][]byte{ 1: []byte(` create domain wt_public_id as text @@ -7828,6 +7828,66 @@ alter table wh_session_connection_accumulating_fact create trigger delete_host_set_subtype after delete on host_plugin_set for each row execute procedure delete_host_set_subtype(); + + -- TODO: Rebuild this table to appropriately cache or sync data from the + -- plugin'ed service. + -- host_plugin_host captures plugin based host data. This is only written to + -- from the controller and is not mutable directly by actions from the end + -- user. + create table host_plugin_host ( + public_id wt_public_id primary key, + catalog_id wt_public_id not null + constraint host_plugin_catalog_fkey + references host_plugin_catalog (public_id) + on delete cascade + on update cascade, + name wt_name, + description text, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + + -- TODO: Break out the address to match the domain model where a host + -- can have multiple addresses and relies on the set to select the + -- prefered one. + address text not null + constraint address_must_be_more_than_2_characters + check(length(trim(address)) > 2) + constraint address_must_be_less_than_256_characters + check(length(trim(address)) < 256), + + constraint host_fkey + foreign key (catalog_id, public_id) + references host (catalog_id, public_id) + on delete cascade + on update cascade, + + constraint host_plugin_host_catalog_id_name_uq + unique(catalog_id, name), + + constraint host_plugin_host_catalog_id_public_id_uq + unique(catalog_id, public_id) + ); + + create trigger update_version_column after update on host_plugin_host + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on host_plugin_host + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on host_plugin_host + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_host + for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + + create trigger insert_host_subtype before insert on host_plugin_host + for each row execute procedure insert_host_subtype(); + + create trigger delete_host_subtype after delete on host_plugin_host + for each row execute procedure delete_host_subtype(); + + insert into oplog_ticket (name, version) values ('host_plugin_catalog', 1), @@ -7906,6 +7966,89 @@ from group by hs.public_id, hc.plugin_id; comment on view host_plugin_host_set_with_value_obj is 'host plugin host set with its associated value objects'; +`), + 17006: []byte(` +-- replaces view from 15/02_wh_rename_host_dimension_org.up.sql + +-- Updates whx_host_dimension_source to support plugin based host and related +-- resources. While this will return rows where there are host_sets where hosts +-- are not in the host sets, this view is only used when looking up a row by +-- host, set, and target so the records where hosts and host sets aren't +-- related beyond the shared catalog will be ignored. +-- TODO: Support a host having multiple addresses. +drop view whx_host_dimension_source; +create view whx_host_dimension_source as +select -- id is the first column in the target view + h.public_id as host_id, + case when sh.public_id is not null then 'static host' + when ph.public_id is not null then 'plugin host' + else 'Unknown' end as host_type, + case when sh.public_id is not null then coalesce(sh.name, 'None') + when ph.public_id is not null then coalesce(ph.name, 'None') + else 'Unknown' end as host_name, + case when sh.public_id is not null then coalesce(sh.description, 'None') + when ph.public_id is not null then coalesce(ph.description, 'None') + else 'Unknown' end as host_description, + + -- TODO: update this to be endpoint address... + coalesce(sh.address, 'Unknown') as host_address, + + hs.public_id as host_set_id, + case when shs.public_id is not null then 'static host set' + when phs.public_id is not null then 'plugin host set' + else 'Unknown' end as host_set_type, + case + when shs.public_id is not null then coalesce(shs.name, 'None') + when phs.public_id is not null then coalesce(phs.name, 'None') + else 'None' + end as host_set_name, + case + when shs.public_id is not null then coalesce(shs.description, 'None') + when phs.public_id is not null then coalesce(phs.description, 'None') + else 'None' + end as host_set_description, + hc.public_id as host_catalog_id, + case when shc.public_id is not null then 'static host catalog' + when phc.public_id is not null then 'plugin host catalog' + else 'Unknown' end as host_catalog_type, + case + when shc.public_id is not null then coalesce(shc.name, 'None') + when phc.public_id is not null then coalesce(phc.name, 'None') + else 'None' + end as host_catalog_name, + case + when shc.public_id is not null then coalesce(shc.description, 'None') + when phc.public_id is not null then coalesce(phc.description, 'None') + else 'None' + end as host_catalog_description, + t.public_id as target_id, + 'tcp target' as target_type, + coalesce(t.name, 'None') as target_name, + coalesce(t.description, 'None') as target_description, + coalesce(t.default_port, 0) as target_default_port_number, + t.session_max_seconds as target_session_max_seconds, + t.session_connection_limit as target_session_connection_limit, + p.public_id as project_id, + coalesce(p.name, 'None') as project_name, + coalesce(p.description, 'None') as project_description, + o.public_id as organization_id, + coalesce(o.name, 'None') as organization_name, + coalesce(o.description, 'None') as organization_description + from host as h + join host_catalog as hc on h.catalog_id = hc.public_id + join host_set as hs on h.catalog_id = hs.catalog_id + join target_host_set as ts on hs.public_id = ts.host_set_id + join target_tcp as t on ts.target_id = t.public_id + join iam_scope as p on t.scope_id = p.public_id and p.type = 'project' + join iam_scope as o on p.parent_id = o.public_id and o.type = 'org' + + left join static_host as sh on sh.public_id = h.public_id + left join host_plugin_host as ph on ph.public_id = h.public_id + left join static_host_catalog as shc on shc.public_id = hc.public_id + left join host_plugin_catalog as phc on phc.public_id = hc.public_id + left join static_host_set as shs on shs.public_id = hs.public_id + left join host_plugin_set as phs on phs.public_id = hs.public_id +; `), 2001: []byte(` -- log_migration entries represent logs generated during migrations diff --git a/internal/host/endpoint.go b/internal/host/endpoint.go new file mode 100644 index 000000000..50f5792fe --- /dev/null +++ b/internal/host/endpoint.go @@ -0,0 +1,9 @@ +package host + +// Endpoint is a struct which identifies an address provided by a host and +// selected as the priority address by the specified host set. +type Endpoint struct { + HostId string + SetId string + Address string +} diff --git a/internal/host/host.go b/internal/host/host.go new file mode 100644 index 000000000..02bc57963 --- /dev/null +++ b/internal/host/host.go @@ -0,0 +1,23 @@ +package host + +import "github.com/hashicorp/boundary/internal/boundary" + +// Catalog contains the common methods across all the different types of host +// catalogs. +type Catalog interface { + boundary.Resource + GetScopeId() string +} + +// Set contains the common methods across all the different types of host sets. +type Set interface { + boundary.Resource + GetCatalogId() string +} + +// Host contains the common methods across all the different types of hosts. +type Host interface { + boundary.Resource + GetCatalogId() string + GetAddress() string +} diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go new file mode 100644 index 000000000..96b431dfb --- /dev/null +++ b/internal/host/plugin/host.go @@ -0,0 +1,76 @@ +package plugin + +import ( + "context" + + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/oplog" + "google.golang.org/protobuf/proto" +) + +// A Host is a temporarily cached plugin based resource. The source of truth +// of the data contained here is provided by the service backing the plugin for +// this host. The plugin for this host can be found by looking at the plugin +// field for this host's host catalog. +type Host struct { + *store.Host + tableName string `gorm:"-"` +} + +// newHost creates a new in memory Host assigned to catalogId with an address. +// Name and description are the only valid options. All other options are +// ignored. +func newHost(ctx context.Context, catalogId, address string, opt ...Option) *Host { + opts := getOpts(opt...) + + h := &Host{ + Host: &store.Host{ + CatalogId: catalogId, + Name: opts.withName, + Description: opts.withDescription, + Address: address, + }, + } + + return h +} + +// TableName returns the table name for the host set. +func (s *Host) TableName() string { + if s.tableName != "" { + return s.tableName + } + return "host_plugin_host" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (s *Host) SetTableName(n string) { + s.tableName = n +} + +func allocHost() *Host { + return &Host{ + Host: &store.Host{}, + } +} + +func (s *Host) clone() *Host { + cp := proto.Clone(s.Host) + hs := &Host{ + Host: cp.(*store.Host), + } + return hs +} + +func (s *Host) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{s.PublicId}, + "resource-type": []string{"plugin-host"}, + "op-type": []string{op.String()}, + } + if s.CatalogId != "" { + metadata["catalog-id"] = []string{s.CatalogId} + } + return metadata +} diff --git a/internal/host/plugin/host_test.go b/internal/host/plugin/host_test.go new file mode 100644 index 000000000..ee7bd501c --- /dev/null +++ b/internal/host/plugin/host_test.go @@ -0,0 +1,202 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHost_Create(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := host.TestPlugin(t, conn, "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + + type args struct { + catalogId string + address string + opts []Option + } + + tests := []struct { + name string + args args + want *Host + wantErr bool + }{ + { + name: "blank-catalogId", + args: args{ + catalogId: "", + address: "foo.bar.com", + }, + want: &Host{Host: &store.Host{ + Address: "foo.bar.com", + }}, + wantErr: true, + }, + { + name: "blank-address", + args: args{ + catalogId: cat.GetPublicId(), + }, + want: &Host{Host: &store.Host{ + CatalogId: cat.GetPublicId(), + }}, + wantErr: true, + }, + { + name: "valid-no-options", + args: args{ + catalogId: cat.GetPublicId(), + address: "foo.bar.com", + }, + want: &Host{ + Host: &store.Host{ + CatalogId: cat.GetPublicId(), + Address: "foo.bar.com", + }, + }, + }, + { + name: "valid-with-name", + args: args{ + catalogId: cat.GetPublicId(), + address: "foo.bar.com", + opts: []Option{ + WithName("test-name"), + }, + }, + want: &Host{ + Host: &store.Host{ + CatalogId: cat.GetPublicId(), + Address: "foo.bar.com", + Name: "test-name", + }, + }, + }, + { + name: "duplicate-name", + args: args{ + catalogId: cat.GetPublicId(), + address: "foo.bar.com", + opts: []Option{ + WithName("test-name"), + }, + }, + want: &Host{ + Host: &store.Host{ + CatalogId: cat.GetPublicId(), + Address: "foo.bar.com", + Name: "test-name", + }, + }, + wantErr: true, + }, + { + name: "valid-duplicate-name-different-catalog", + args: args{ + catalogId: cat2.GetPublicId(), + address: "foo.bar.com", + opts: []Option{ + WithName("test-name"), + }, + }, + want: &Host{ + Host: &store.Host{ + CatalogId: cat2.GetPublicId(), + Address: "foo.bar.com", + Name: "test-name", + }, + }, + }, + { + name: "valid-with-description", + args: args{ + catalogId: cat.GetPublicId(), + address: "foo.bar.com", + opts: []Option{ + WithDescription("test-description"), + }, + }, + want: &Host{ + Host: &store.Host{ + CatalogId: cat.GetPublicId(), + Address: "foo.bar.com", + Description: "test-description", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + got := newHost(ctx, tt.args.catalogId, tt.args.address, tt.args.opts...) + require.NotNil(t, got) + assert.Emptyf(t, got.PublicId, "PublicId set") + assert.Equal(t, tt.want, got) + + id, err := newHostId(ctx, tt.name, tt.name) + assert.NoError(t, err) + + tt.want.PublicId = id + got.PublicId = id + + w := db.New(conn) + err = w.Create(context.Background(), got) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestHost_SetTableName(t *testing.T) { + defaultTableName := "host_plugin_host" + tests := []struct { + name string + initialName string + setNameTo string + want string + }{ + { + name: "new-name", + initialName: "", + setNameTo: "new-name", + want: "new-name", + }, + { + name: "reset to default", + initialName: "initial", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + def := &Host{ + Host: &store.Host{}, + } + require.Equal(defaultTableName, def.TableName()) + s := &Host{ + Host: &store.Host{}, + tableName: tt.initialName, + } + s.SetTableName(tt.setNameTo) + assert.Equal(tt.want, s.TableName()) + }) + } +} diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go index fa384bded..0516abead 100644 --- a/internal/host/plugin/ids.go +++ b/internal/host/plugin/ids.go @@ -17,9 +17,9 @@ func init() { // PublicId prefixes for the resources in the plugin package. const ( - // TODO: Pull these out of being constants and have them derivable at run time. HostCatalogPrefix = "hc" HostSetPrefix = "hs" + HostPrefix = "h" Subtype = subtypes.Subtype("plugin") ) @@ -39,3 +39,18 @@ func newHostSetId(ctx context.Context) (string, error) { } return id, nil } + +func newHostId(ctx context.Context, catalogId, externalId string) (string, error) { + const op = "plugin.newHostId" + if catalogId == "" { + return "", errors.New(ctx, errors.InvalidParameter, op, "missing catalog id") + } + if externalId == "" { + return "", errors.New(ctx, errors.InvalidParameter, op, "missing external id") + } + id, err := db.NewPublicId(HostPrefix, db.WithPrngValues([]string{catalogId, externalId})) + if err != nil { + return "", errors.Wrap(ctx, err, op) + } + return id, nil +} diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 89cd3dbaf..416815206 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -151,10 +151,11 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } c, err := r.getCatalog(ctx, id) + if errors.IsNotFoundError(err) { + return nil, nil, nil + } + if err != nil { - if errors.IsNotFoundError(err) { - return nil, nil, nil - } return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) } plg, err := r.getPlugin(ctx, c.GetPluginId()) @@ -206,16 +207,6 @@ func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, e return c, nil } -func (r *Repository) getPlugin(ctx context.Context, id string) (*hostplugin.Plugin, error) { - const op = "plugin.(Repository).getPlugin" - c := hostplugin.NewPlugin() - c.PublicId = id - if err := r.reader.LookupByPublicId(ctx, c); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) - } - return c, nil -} - // getPersistedDataForCatalog returns the persisted data for a catalog if // present. c must have a valid Public Id and Scope Id set. // TODO: consider merging the functions for getting catalog and persisted data into a view. @@ -252,6 +243,19 @@ func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCata return &plgpb.HostCatalogPersisted{Secrets: secrets}, nil } +func (r *Repository) getPlugin(ctx context.Context, plgId string) (*hostplugin.Plugin, error) { + const op = "plugin.(Repository).getPlugin" + if plgId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin id") + } + plg := hostplugin.NewPlugin() + plg.PublicId = plgId + if err := r.reader.LookupByPublicId(ctx, plg); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get host plugin with id %q", plgId))) + } + return plg, nil +} + // toPluginCatalog returns a host catalog, with it's secret if available, in the format expected // by the host plugin system. func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, error) { diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 72e58ecd4..74c798a72 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -12,8 +12,10 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" + hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -192,9 +194,6 @@ func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).getSets" - const aggregateDelimiter = "|" - const priorityDelimiter = "=" - if publicId == "" && catalogId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing search criteria: both host set id and catalog id are empty") } @@ -245,55 +244,9 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str sets := make([]*HostSet, 0, len(aggHostSets)) for _, agg := range aggHostSets { - hs := allocHostSet() - hs.PublicId = agg.PublicId - hs.CatalogId = agg.CatalogId - hs.Name = agg.Name - hs.Description = agg.Description - hs.CreateTime = agg.CreateTime - hs.UpdateTime = agg.UpdateTime - hs.Version = agg.Version - hs.Attributes = agg.Attributes - if agg.PreferredEndpoints != "" { - eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter) - if len(eps) > 0 { - // We want to protect against someone messing with the DB - // and not panic, so we do a bit of a dance here - var sortErr error - sort.Slice(eps, func(i, j int) bool { - epi := strings.Split(eps[i], priorityDelimiter) - if len(epi) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i])) - return false - } - epj := strings.Split(eps[j], priorityDelimiter) - if len(epj) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j])) - return false - } - indexi, err := strconv.Atoi(epi[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - indexj, err := strconv.Atoi(epj[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - return indexi < indexj - }) - if sortErr != nil { - return nil, nil, sortErr - } - for i, ep := range eps { - // At this point they're in the correct order, but we still - // have to strip off the priority - eps[i] = strings.Split(ep, priorityDelimiter)[1] - } - - hs.PreferredEndpoints = eps - } + hs, err := agg.toHostSet(ctx) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) } sets = append(sets, hs) } @@ -323,6 +276,62 @@ type hostSetAgg struct { PreferredEndpoints string } +func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { + const op = "plugin.(hostSetAgg).toHostSet" + const aggregateDelimiter = "|" + const priorityDelimiter = "=" + hs := allocHostSet() + hs.PublicId = agg.PublicId + hs.CatalogId = agg.CatalogId + hs.Name = agg.Name + hs.Description = agg.Description + hs.CreateTime = agg.CreateTime + hs.UpdateTime = agg.UpdateTime + hs.Version = agg.Version + hs.Attributes = agg.Attributes + if agg.PreferredEndpoints != "" { + eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter) + if len(eps) > 0 { + // We want to protect against someone messing with the DB + // and not panic, so we do a bit of a dance here + var sortErr error + sort.Slice(eps, func(i, j int) bool { + epi := strings.Split(eps[i], priorityDelimiter) + if len(epi) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i])) + return false + } + epj := strings.Split(eps[j], priorityDelimiter) + if len(epj) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j])) + return false + } + indexi, err := strconv.Atoi(epi[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + indexj, err := strconv.Atoi(epj[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + return indexi < indexj + }) + if sortErr != nil { + return nil, sortErr + } + for i, ep := range eps { + // At this point they're in the correct order, but we still + // have to strip off the priority + eps[i] = strings.Split(ep, priorityDelimiter)[1] + } + hs.PreferredEndpoints = eps + } + } + return hs, nil +} + // TableName returns the table name for gorm func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" } @@ -344,3 +353,183 @@ func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { } return hs, nil } + +// Endpoints provides all the endpoints available for a given set id. +// An error is returned if the set, related catalog, or related plugin are +// unable to be retrieved. If a host does not contain an addressible endpoint +// it is not included in the resulting slice of endpoints. +func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.Endpoint, error) { + const op = "plugin.(Repository).Endpoints" + if len(setIds) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no set ids") + } + var setAggs []*hostSetAgg + if err := r.reader.SearchWhere(ctx, &setAggs, "public_id in (?)", []interface{}{setIds}); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve sets %v", setIds))) + } + if len(setAggs) == 0 { + return nil, nil + } + + type setInfo struct { + preferredEndpoint endpoint.Option + plgSet *pb.HostSet + } + + type catalogInfo struct{ + publicId string + plg plgpb.HostPluginServiceServer + setInfos map[string]*setInfo + plgCat *hcpb.HostCatalog + persisted *plgpb.HostCatalogPersisted + } + + catalogInfos := make(map[string]*catalogInfo) + for _, ag := range setAggs { + ci, ok := catalogInfos[ag.CatalogId] + if !ok { + ci = &catalogInfo{ + publicId: ag.CatalogId, + setInfos: make(map[string]*setInfo), + } + } + ci.plg, ok = r.plugins[ag.PluginId] + if !ok { + return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", ag.PluginId)) + } + + s, err := ag.toHostSet(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + si, ok := ci.setInfos[s.GetPublicId()] + if !ok { + si = &setInfo{} + } + si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints()) + si.plgSet, err = toPluginSet(ctx, s) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId()))) + } + + ci.setInfos[s.GetPublicId()] = si + catalogInfos[ag.CatalogId] = ci + } + + catIds := make([]string, 0, len(catalogInfos)) + for k := range catalogInfos { + catIds = append(catIds, k) + } + var cats []*HostCatalog + if err := r.reader.SearchWhere(ctx, &cats, "public_id in (?)", []interface{}{catIds}); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve catalogs %v", catIds))) + } + if len(cats) == 0 { + return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "no catalogs returned for retrieved sets") + } + for _, c := range cats { + ci, ok := catalogInfos[c.GetPublicId()] + if !ok { + return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "catalog returned when no set requested it") + } + plgCat, err := toPluginCatalog(ctx, c) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("storage to plugin catalog conversion")) + } + ci.plgCat = plgCat + + // TODO: Do these looksups from the DB in bulk instead of individually. + per, err := r.getPersistedDataForCatalog(ctx, c) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("persisted catalog lookup failed")) + } + ci.persisted = per + catalogInfos[c.GetPublicId()] = ci + } + + // For writing the hosts to the db so data warehouse doesn't complain + var hosts []interface{} + hostIds := map[string]bool{} + var es []*host.Endpoint + for _, ci := range catalogInfos { + + var sets []*pb.HostSet + for _, si := range ci.setInfos { + sets = append(sets, si.plgSet) + } + + resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ + Catalog: ci.plgCat, + Sets: sets, + //Persisted: ci.persisted, + }) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + for _, h := range resp.GetHosts() { + hostId, err := newHostId(ctx, ci.publicId, h.GetExternalId()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + for _, sId := range h.GetSetIds() { + var opts []endpoint.Option + if len(h.GetIpAddresses()) > 0 { + opts = append(opts, endpoint.WithIpAddrs(h.GetIpAddresses())) + } + if len(h.GetDnsNames()) > 0 { + opts = append(opts, endpoint.WithIpAddrs(h.GetDnsNames())) + } + + si, ok := ci.setInfos[sId] + if !ok { + return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "host is reporting it's part of a set we didn't query for") + } + pref, err := endpoint.NewPreferencer(ctx, si.preferredEndpoint) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId))) + } + addr, err := pref.Choose(ctx, opts...) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + if addr == "" { + continue + } + es = append(es, &host.Endpoint{ + HostId: hostId, + SetId: sId, + Address: addr, + }) + + if _, ok := hostIds[hostId]; !ok { + hostIds[hostId] = true + host := newHost(ctx, ci.publicId, addr) + host.PublicId = hostId + hosts = append(hosts, host) + } + } + } + } + + if len(hosts) > 0 { + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + if _, err := r.writer.DeleteItems(ctx, hosts); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("couldn't delete existing hosts")) + } + if err := r.writer.CreateItems(ctx, hosts); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("can't persist hosts")) + } + return nil + }) + if err != nil { + return nil, err + } + } + return es, nil +} diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 3ffb12142..d875ca999 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "sort" "strings" "testing" "time" @@ -365,6 +366,139 @@ func TestRepository_LookupSet(t *testing.T) { } } +func TestRepository_Endpoints(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "endpoints") + + hostlessCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): &TestPluginServer{ + ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + if req.Catalog.GetId() == hostlessCatalog.GetPublicId() { + return &plgpb.ListHostsResponse{}, nil + } + var setIds []string + for _, set := range req.GetSets() { + setIds = append(setIds, set.GetId()) + } + return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ + { + SetIds: setIds, + ExternalId: "test", + IpAddresses: []string{"10.0.0.5", "192.168.0.5"}, + DnsNames: nil, + }, + }}, nil + }, + }, + } + + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + hostSet10 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) + hostSet192 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) + hostSet100 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) + hostlessSet := TestSet(t, conn, kms, hostlessCatalog, plgm) + + tests := []struct { + name string + setIds []string + want []*host.Endpoint + wantIsErr errors.Code + }{ + { + name: "with-no-set-id", + wantIsErr: errors.InvalidParameter, + }, + { + name: "with-set10", + setIds: []string{hostSet10.GetPublicId()}, + want: []*host.Endpoint{ + { + HostId: func() string { + s, err := newHostId(ctx, catalog.GetPublicId(), "test") + require.NoError(t, err) + return s + }(), + SetId: hostSet10.GetPublicId(), + Address: "10.0.0.5", + }, + }, + }, + { + name: "with-different-set", + setIds: []string{hostSet192.GetPublicId()}, + want: []*host.Endpoint{ + { + HostId: func() string { + s, err := newHostId(ctx, catalog.GetPublicId(), "test") + require.NoError(t, err) + return s + }(), + SetId: hostSet192.GetPublicId(), + Address: "192.168.0.5", + }, + }, + }, + { + name: "with-all-addresses-filtered-set", + setIds: []string{hostSet100.GetPublicId()}, + want: nil, + }, + { + name: "with-no-hosts-from-plugin", + setIds: []string{hostlessSet.GetPublicId()}, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms, plgm) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.Endpoints(ctx, tt.setIds) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(err) + if tt.want == nil { + return + } + + sort.Slice(tt.want, func(i, j int) bool { + return tt.want[i].HostId < tt.want[j].HostId + }) + sort.Slice(got, func(i, j int) bool { + return got[i].HostId < got[j].HostId + }) + assert.Empty(cmp.Diff(got, tt.want, protocmp.Transform())) + + // TODO: Remove this once we no longer persist all host lookup calls + // when retrieving the endpoints. + for _, ep := range got { + h := allocHost() + h.PublicId = ep.HostId + require.NoError(rw.LookupByPublicId(ctx, h)) + + assert.Equal(uint32(1), h.Version) + assert.Equal(ep.HostId, h.PublicId) + assert.Equal(ep.Address, h.Address) + assert.Equal(catalog.GetPublicId(), h.GetCatalogId()) + } + }) + } +} + func TestRepository_ListSets(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 2b88558b7..1f2d7dcec 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -155,6 +155,127 @@ func (x *HostCatalog) GetAttributes() []byte { return nil } +type Host struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // address is the IP Address or DNS name of the host. + // @inject_tag: `gorm:"default:null"` + Address string `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` +} + +func (x *Host) Reset() { + *x = Host{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Host) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Host) ProtoMessage() {} + +func (x *Host) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 Host.ProtoReflect.Descriptor instead. +func (*Host) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1} +} + +func (x *Host) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Host) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Host) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Host) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Host) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Host) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + +func (x *Host) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Host) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + type HostSet struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -194,7 +315,7 @@ type HostSet struct { func (x *HostSet) Reset() { *x = HostSet{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -207,7 +328,7 @@ func (x *HostSet) String() string { func (*HostSet) ProtoMessage() {} func (x *HostSet) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -220,7 +341,7 @@ func (x *HostSet) ProtoReflect() protoreflect.Message { // Deprecated: Use HostSet.ProtoReflect.Descriptor instead. func (*HostSet) Descriptor() ([]byte, []int) { - return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1} + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2} } func (x *HostSet) GetPublicId() string { @@ -316,7 +437,7 @@ type HostCatalogSecret struct { func (x *HostCatalogSecret) Reset() { *x = HostCatalogSecret{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -329,7 +450,7 @@ func (x *HostCatalogSecret) String() string { func (*HostCatalogSecret) ProtoMessage() {} func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -342,7 +463,7 @@ func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message { // Deprecated: Use HostCatalogSecret.ProtoReflect.Descriptor instead. func (*HostCatalogSecret) Descriptor() ([]byte, []int) { - return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2} + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3} } func (x *HostCatalogSecret) GetCatalogId() string { @@ -429,57 +550,77 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, - 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x04, 0x48, 0x6f, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, + 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, - 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, - 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, - 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, + 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, + 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, + 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -494,25 +635,28 @@ func file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP() []byt return file_controller_storage_host_plugin_store_v1_host_proto_rawDescData } -var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{ (*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog - (*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet - (*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*Host)(nil), // 1: controller.storage.host.plugin.store.v1.Host + (*HostSet)(nil), // 2: controller.storage.host.plugin.store.v1.HostSet + (*HostCatalogSecret)(nil), // 3: controller.storage.host.plugin.store.v1.HostCatalogSecret + (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{ - 3, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 4, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 2: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 3: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 4: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 5: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 6: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // 7: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_controller_storage_host_plugin_store_v1_host_proto_init() } @@ -534,7 +678,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { } } file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HostSet); i { + switch v := v.(*Host); i { case 0: return &v.state case 1: @@ -546,6 +690,18 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { } } file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HostCatalogSecret); i { case 0: return &v.state @@ -564,7 +720,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_plugin_store_v1_host_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/host/static/repository_host_set.go b/internal/host/static/repository_host_set.go index a3d986924..b28138f4c 100644 --- a/internal/host/static/repository_host_set.go +++ b/internal/host/static/repository_host_set.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/internal/db" dbcommon "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" ) @@ -182,6 +183,26 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, return returnedHostSet, hosts, rowsUpdated, nil } +// Endpoints returns a slice of host.Endpoint for the provided set id. +// If there are no hosts in the provided set id the slice is empty. +// If the set does not exist an error is returned. +func (r *Repository) Endpoints(ctx context.Context, setId string) ([]*host.Endpoint, error) { + const op = "static.(Repository).Endpoints" + _, hs, err := r.lookupSet(ctx, setId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + var es []*host.Endpoint + for _, h := range hs { + es = append(es, &host.Endpoint{ + HostId: h.GetPublicId(), + SetId: setId, + Address: h.GetAddress(), + }) + } + return es, err +} + // LookupSet will look up a host set in the repository and return the host // set and the hosts assigned to the host set. If the host set is not // found, it will return nil, nil, nil. The WithLimit option can be used to @@ -191,6 +212,11 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...Opti if publicId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } + return r.lookupSet(ctx, publicId, opt...) +} + +func (r *Repository) lookupSet(ctx context.Context, publicId string, opt ...Option) (*HostSet, []*Host, error) { + const op = "static.(Repository).lookupSet" opts := getOpts(opt...) limit := r.defaultLimit if opts.withLimit != 0 { @@ -201,29 +227,20 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...Opti s := allocHostSet() s.PublicId = publicId + if err := r.reader.LookupByPublicId(ctx, s); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil, nil + } + return nil, nil, errors.Wrap(ctx, err, op) + } var hosts []*Host - _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, _ db.Writer) error { - if err := reader.LookupByPublicId(ctx, s); err != nil { - if errors.IsNotFoundError(err) { - s = nil - return nil - } - if err != nil { - return errors.Wrap(ctx, err, op) - } - return nil - } - var err error - hosts, err = getHosts(ctx, reader, s.PublicId, limit) - if err != nil { - return errors.Wrap(ctx, err, op) - } - return nil - }) + var err error + if hosts, err = getHosts(ctx, r.reader, s.PublicId, limit); err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", s.PublicId))) } - return s, hosts, nil } diff --git a/internal/host/subtype.go b/internal/host/subtype.go index 2316d0785..b9da2e5c6 100644 --- a/internal/host/subtype.go +++ b/internal/host/subtype.go @@ -1,7 +1,6 @@ package host import ( - "github.com/hashicorp/boundary/internal/boundary" "github.com/hashicorp/boundary/internal/types/subtypes" ) @@ -25,22 +24,3 @@ func SubtypeFromId(id string) subtypes.Subtype { func Register(subtype subtypes.Subtype, prefixes ...string) error { return registry.Register(subtype, prefixes...) } - -// Catalog contains the common methods across all the different types of host catalogs. -type Catalog interface { - boundary.Resource - GetScopeId() string -} - -// Set contains the common methods across all the different types of host sets. -type Set interface { - boundary.Resource - GetCatalogId() string -} - -// Host contains the common methods across all the different types of hosts. -type Host interface { - boundary.Resource - GetCatalogId() string - GetAddress() string -} diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 37f1c2141..5d9722bea 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -47,6 +47,42 @@ message HostCatalog { bytes attributes = 9; } +message Host { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + string name = 4; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5; + + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + string catalog_id = 6; + + // version allows optimistic locking of the resource + // @inject_tag: `gorm:"default:null"` + uint32 version = 7; + + // address is the IP Address or DNS name of the host. + // @inject_tag: `gorm:"default:null"` + string address = 8; +} + message HostSet { // public_id is a surrogate key suitable for use in a public API. // @inject_tag: `gorm:"primary_key"` diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 94e259d6c..d59a39c82 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -142,11 +142,13 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er return nil, fmt.Errorf("failed to register user service handler: %w", err) } ts, err := targets.NewService( + ctx, c.kms, c.TargetRepoFn, c.IamRepoFn, c.ServersRepoFn, c.SessionRepoFn, + c.PluginHostRepoFn, c.StaticHostRepoFn, c.VaultCredentialRepoFn) if err != nil { diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index d28371373..f1ab5844f 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -91,6 +91,7 @@ type Service struct { iamRepoFn common.IamRepoFactory serversRepoFn common.ServersRepoFactory sessionRepoFn common.SessionRepoFactory + pluginHostRepoFn common.PluginHostRepoFactory staticHostRepoFn common.StaticRepoFactory vaultCredRepoFn common.VaultCredentialRepoFactory kmsCache *kms.Kms @@ -98,37 +99,43 @@ type Service struct { // NewService returns a target service which handles target related requests to boundary. func NewService( + ctx context.Context, kmsCache *kms.Kms, repoFn common.TargetRepoFactory, iamRepoFn common.IamRepoFactory, serversRepoFn common.ServersRepoFactory, sessionRepoFn common.SessionRepoFactory, + pluginHostRepoFn common.PluginHostRepoFactory, staticHostRepoFn common.StaticRepoFactory, vaultCredRepoFn common.VaultCredentialRepoFactory) (Service, error) { const op = "targets.NewService" if repoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing target repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing target repository") } if iamRepoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository") } if serversRepoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing servers repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing servers repository") } if sessionRepoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing session repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing session repository") + } + if pluginHostRepoFn == nil { + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing plugin host repository") } if staticHostRepoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static host repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static host repository") } if vaultCredRepoFn == nil { - return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing vault credential repository") + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository") } return Service{ repoFn: repoFn, iamRepoFn: iamRepoFn, serversRepoFn: serversRepoFn, sessionRepoFn: sessionRepoFn, + pluginHostRepoFn: pluginHostRepoFn, staticHostRepoFn: staticHostRepoFn, vaultCredRepoFn: vaultCredRepoFn, kmsCache: kmsCache, @@ -940,55 +947,65 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession "No workers are available to handle this session, or all have been filtered.") } - // First, fetch all available hosts. Unless one was chosen in the request, - // we will pick one at random. - type compoundHost struct { - hostSetId string - hostId string - } - - var chosenId *compoundHost requestedId := req.GetHostId() staticHostRepo, err := s.staticHostRepoFn() if err != nil { return nil, err } + pluginHostRepo, err := s.pluginHostRepoFn() + if err != nil { + return nil, err + } - hostIds := make([]compoundHost, 0, len(hostSources)*10) - -HostSourceIterationLoop: + var pluginHostSetIds []string + var endpoints []*host.Endpoint for _, hSource := range hostSources { hsId := hSource.Id() + // FIXME: read in type from DB rather than rely on prefix switch host.SubtypeFromId(hsId) { case static.Subtype: - _, hosts, err := staticHostRepo.LookupSet(ctx, hsId) + eps, err := staticHostRepo.Endpoints(ctx, hsId) if err != nil { return nil, err } - for _, host := range hosts { - compoundId := compoundHost{hostSetId: hsId, hostId: host.PublicId} - hostIds = append(hostIds, compoundId) - if host.PublicId == requestedId { - chosenId = &compoundId - break HostSourceIterationLoop - } + endpoints = append(endpoints, eps...) + default: + // Batch the plugin host set ids since each round trip to the plugin + // has the potential to be expensive. + pluginHostSetIds = append(pluginHostSetIds, hsId) + } + } + if len(pluginHostSetIds) > 0 { + eps, err := pluginHostRepo.Endpoints(ctx, pluginHostSetIds) + if err != nil { + return nil, err + } + endpoints = append(endpoints, eps...) + } + + var chosenEndpoint *host.Endpoint + if requestedId != "" { + for _, ep := range endpoints { + if ep.HostId == requestedId { + chosenEndpoint = ep } } - } - if requestedId != "" && chosenId == nil { - // We didn't find it - return nil, handlers.InvalidArgumentErrorf( - "Errors in provided fields.", - map[string]string{ - "host_id": "The requested host id is not available.", - }) - } - if chosenId == nil { - if len(hostIds) == 0 { - // No hosts were found, error - return nil, handlers.NotFoundErrorf("No hosts found from available target host sources.") + if chosenEndpoint == nil { + // We didn't find it + return nil, handlers.InvalidArgumentErrorf( + "Errors in provided fields.", + map[string]string{ + "host_id": "The requested host id is not available.", + }) } - chosenId = &hostIds[rand.Intn(len(hostIds))] + } + + if chosenEndpoint == nil { + if len(endpoints) == 0 { + // No hosts were found, error + return nil, handlers.NotFoundErrorf("No endpoint found from available target host sources.") + } + chosenEndpoint = endpoints[rand.Intn(len(endpoints))] } // Generate the endpoint URL @@ -996,22 +1013,10 @@ HostSourceIterationLoop: Scheme: t.GetType(), } defaultPort := t.GetDefaultPort() - var endpointHost string - switch host.SubtypeFromId(chosenId.hostId) { - case static.Subtype: - h, err := staticHostRepo.LookupHost(ctx, chosenId.hostId) - if err != nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "errors looking up host") - } - endpointHost = h.Address - if endpointHost == "" { - return nil, stderrors.New("host had empty address") - } - } if defaultPort != 0 { - endpointUrl.Host = fmt.Sprintf("%s:%d", endpointHost, defaultPort) + endpointUrl.Host = fmt.Sprintf("%s:%d", chosenEndpoint.Address, defaultPort) } else { - endpointUrl.Host = endpointHost + endpointUrl.Host = chosenEndpoint.Address } var reqs []credential.Request @@ -1028,9 +1033,9 @@ HostSourceIterationLoop: expTime.Seconds += int64(t.GetSessionMaxSeconds()) sessionComposition := session.ComposedOf{ UserId: authResults.UserId, - HostId: chosenId.hostId, + HostId: chosenEndpoint.HostId, TargetId: t.GetPublicId(), - HostSetId: chosenId.hostSetId, + HostSetId: chosenEndpoint.SetId, AuthTokenId: authResults.AuthTokenId, ScopeId: authResults.Scope.Id, Endpoint: endpointUrl.String(), @@ -1122,7 +1127,7 @@ HostSourceIterationLoop: Type: t.GetType(), Certificate: sess.Certificate, PrivateKey: privKey, - HostId: chosenId.hostId, + HostId: chosenEndpoint.HostId, Endpoint: endpointUrl.String(), WorkerInfo: workers, ConnectionLimit: t.GetSessionConnectionLimit(), @@ -1141,8 +1146,8 @@ HostSourceIterationLoop: Type: t.GetType(), AuthorizationToken: string(encodedMarshaledSad), UserId: authResults.UserId, - HostId: chosenId.hostId, - HostSetId: chosenId.hostSetId, + HostId: chosenEndpoint.HostId, + HostSetId: chosenEndpoint.SetId, Endpoint: endpointUrl.String(), Credentials: creds, } diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index cedd2b038..7b6e19936 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -88,10 +88,13 @@ func testService(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wra staticHostRepoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginHostRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } - return targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn) + return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) } func TestGet(t *testing.T) { @@ -2586,6 +2589,33 @@ func TestAuthorizeSession(t *testing.T) { return authtoken.NewRepository(rw, rw, kms) } + plg := host.TestPlugin(t, conn, "test") + plgm := map[string]plgpb.HostPluginServiceServer{ + plg.GetPublicId(): plugin.TestPluginServer{ + ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + var setIds []string + for _, set := range req.GetSets() { + setIds = append(setIds, set.GetId()) + } + return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ + { + SetIds: setIds, + ExternalId: "test", + IpAddresses: []string{"10.0.0.1", "192.168.0.1"}, + }, + { + SetIds: setIds, + ExternalId: "test2", + IpAddresses: []string{"10.1.1.1", "192.168.1.1"}, + }, + }}, nil + }, + }, + } + pluginHostRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, plgm) + } + org, proj := iam.TestScopes(t, iamRepo) at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), @@ -2603,21 +2633,16 @@ func TestAuthorizeSession(t *testing.T) { _ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId()) _ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*") - s, err := targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn) + s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) require.NoError(t, err) - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] - hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - _ = static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) + shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + _ = static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{h}) - apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - HostSetIds: []string{hs.GetPublicId()}, - }) - require.NoError(t, err) + phc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + phs := plugin.TestSet(t, conn, kms, phc, plgm, plugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"})) v := vault.NewTestVaultServer(t) v.MountPKI(t) @@ -2638,95 +2663,135 @@ func TestAuthorizeSession(t *testing.T) { }}) require.NoError(t, err) - _, err = s.AddTargetCredentialSources(ctx, - &pbs.AddTargetCredentialSourcesRequest{ - Id: tar.GetPublicId(), - ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()}, - Version: apiTar.GetItem().GetVersion(), + cases := []struct { + name string + hostSourceId string + credSourceId string + wantedHostId string + wantedEndpoint string + }{ + { + name: "static host", + hostSourceId: shs.GetPublicId(), + credSourceId: clsResp.GetItem().GetId(), + wantedHostId: h.GetPublicId(), + wantedEndpoint: h.GetAddress(), + }, + { + name: "plugin host", + hostSourceId: phs.GetPublicId(), + credSourceId: clsResp.GetItem().GetId(), + wantedHostId: "?", + wantedEndpoint: "10.0.0.1", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), tc.name) + apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSetIds: []string{tc.hostSourceId}, + }) + require.NoError(t, err) + _, err = s.AddTargetCredentialSources(ctx, + &pbs.AddTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()}, + Version: apiTar.GetItem().GetVersion(), + }) + require.NoError(t, err) + + // Tell our DB that there is a worker ready to serve the data + workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, &sync.Map{}, kms) + _, err = workerService.Status(ctx, &spbs.StatusRequest{ + Worker: &spb.Server{ + PrivateId: "testworker", + Address: "localhost:8457", + }, + }) + require.NoError(t, err) + + asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ + Id: tar.GetPublicId(), + }) + require.NoError(t, err) + asRes2, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ + Id: tar.GetPublicId(), + }) + require.NoError(t, err) + assert.NotEmpty(t, cmp.Diff(asRes1.GetItem().GetCredentials(), asRes2.GetItem().GetCredentials(), protocmp.Transform()), + "the credentials aren't unique per request authorized session") + + wantedHostId := tc.wantedHostId + if tc.wantedHostId == "?" { + wantedHostId = asRes2.GetItem().GetHostId() + } + + want := &pb.SessionAuthorization{ + Scope: &scopes.ScopeInfo{ + Id: proj.GetPublicId(), + Type: proj.GetType(), + Name: proj.GetName(), + Description: proj.GetDescription(), + ParentScopeId: proj.GetParentId(), + }, + TargetId: tar.GetPublicId(), + UserId: at.GetIamUserId(), + HostSetId: tc.hostSourceId, + HostId: wantedHostId, + Type: "tcp", + Endpoint: fmt.Sprintf("tcp://%s", tc.wantedEndpoint), + Credentials: []*pb.SessionCredential{{ + CredentialLibrary: &pb.CredentialLibrary{ + Id: clsResp.GetItem().GetId(), + Name: clsResp.GetItem().GetName().GetValue(), + Description: clsResp.GetItem().GetDescription().GetValue(), + CredentialStoreId: store.GetPublicId(), + Type: vault.Subtype.String(), + }, + CredentialSource: &pb.CredentialSource{ + Id: clsResp.GetItem().GetId(), + Name: clsResp.GetItem().GetName().GetValue(), + Description: clsResp.GetItem().GetDescription().GetValue(), + CredentialStoreId: store.GetPublicId(), + Type: vault.Subtype.String(), + }, + }}, + // TODO: validate the contents of the authorization token is what is expected + } + wantSecret := map[string]interface{}{ + "certificate": "-----BEGIN CERTIFICATE-----\n", + "issuing_ca": "-----BEGIN CERTIFICATE-----\n", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\n", + "private_key_type": "rsa", + } + got := asRes1.GetItem() + + require.Len(t, got.GetCredentials(), 1) + + gotCred := got.Credentials[0] + require.NotNil(t, gotCred.Secret) + assert.NotEmpty(t, gotCred.Secret.Raw) + dSec := decodeJsonSecret(t, gotCred.Secret.Raw) + require.NoError(t, err) + require.Equal(t, dSec, gotCred.Secret.Decoded.AsMap()) + for k, v := range wantSecret { + gotV, ok := dSec[k] + require.True(t, ok) + assert.Truef(t, strings.HasPrefix(gotV.(string), v.(string)), "%q:%q doesn't have prefix %q", k, gotV, v) + } + gotCred.Secret = nil + + got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil + assert.Empty(t, cmp.Diff(got, want, protocmp.Transform())) }) - require.NoError(t, err) - - // Tell our DB that there is a worker ready to serve the data - workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, &sync.Map{}, kms) - _, err = workerService.Status(ctx, &spbs.StatusRequest{ - Worker: &spb.Server{ - PrivateId: "testworker", - Address: "localhost:8457", - }, - }) - require.NoError(t, err) - - asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ - Id: tar.GetPublicId(), - }) - require.NoError(t, err) - asRes2, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ - Id: tar.GetPublicId(), - }) - require.NoError(t, err) - assert.NotEmpty(t, cmp.Diff(asRes1.GetItem().GetCredentials(), asRes2.GetItem().GetCredentials(), protocmp.Transform()), - "the credentials aren't unique per request authorized session") - - want := &pb.SessionAuthorization{ - Scope: &scopes.ScopeInfo{ - Id: proj.GetPublicId(), - Type: proj.GetType(), - Name: proj.GetName(), - Description: proj.GetDescription(), - ParentScopeId: proj.GetParentId(), - }, - TargetId: tar.GetPublicId(), - UserId: at.GetIamUserId(), - HostSetId: hs.GetPublicId(), - HostId: h.GetPublicId(), - Type: "tcp", - Endpoint: fmt.Sprintf("tcp://%s", h.GetAddress()), - Credentials: []*pb.SessionCredential{{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: clsResp.GetItem().GetId(), - Name: clsResp.GetItem().GetName().GetValue(), - Description: clsResp.GetItem().GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), - Type: vault.Subtype.String(), - }, - CredentialSource: &pb.CredentialSource{ - Id: clsResp.GetItem().GetId(), - Name: clsResp.GetItem().GetName().GetValue(), - Description: clsResp.GetItem().GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), - Type: vault.Subtype.String(), - }, - }}, - // TODO: validate the contents of the authorization token is what is expected } - wantSecret := map[string]interface{}{ - "certificate": "-----BEGIN CERTIFICATE-----\n", - "issuing_ca": "-----BEGIN CERTIFICATE-----\n", - "private_key": "-----BEGIN RSA PRIVATE KEY-----\n", - "private_key_type": "rsa", - } - got := asRes1.GetItem() - - require.Len(t, got.GetCredentials(), 1) - - gotCred := got.Credentials[0] - require.NotNil(t, gotCred.Secret) - assert.NotEmpty(t, gotCred.Secret.Raw) - dSec := decodeJsonSecret(t, gotCred.Secret.Raw) - require.NoError(t, err) - require.Equal(t, dSec, gotCred.Secret.Decoded.AsMap()) - for k, v := range wantSecret { - gotV, ok := dSec[k] - require.True(t, ok) - assert.Truef(t, strings.HasPrefix(gotV.(string), v.(string)), "%q:%q doesn't have prefix %q", k, gotV, v) - } - gotCred.Secret = nil - - got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil - assert.Empty(t, cmp.Diff(got, want, protocmp.Transform())) } func TestAuthorizeSession_Errors(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -2749,6 +2814,9 @@ func TestAuthorizeSession_Errors(t *testing.T) { staticHostRepoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + pluginHostRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } @@ -2757,12 +2825,12 @@ func TestAuthorizeSession_Errors(t *testing.T) { } org, proj := iam.TestScopes(t, iamRepo) - s, err := targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn) + s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) require.NoError(t, err) // Authorized user gets full permissions at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) - ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), + ctx = auth.NewVerifierContext(requests.NewRequestContext(context.Background()), iamRepoFn, atRepoFn, serversRepoFn, From a834a147850bbde6100aaa7b0e692812cda4f2e8 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 26 Sep 2021 11:48:43 -0400 Subject: [PATCH 24/71] Add attribute flag building and parsing (#1562) This adds a few command sketches but that's not the main point of the PR and filling those out will come in a different PR. This adds a new flag type that can combine inputs to a single target slice, retaining the ability to distinguish where it came from but also keep ordering across different flag names. It also implements a function that does the heuristic parsing with fallback. --- api/hostsets/option.gen.go | 12 + internal/cmd/base/base.go | 4 + internal/cmd/base/flags.go | 118 +++++ internal/cmd/commands.go | 12 + .../commands/hostcatalogscmd/plugin_funcs.go | 33 ++ .../plugin_hostcatalogs.gen.go | 260 +++++++++++ .../commands/hostcatalogscmd/static_funcs.go | 2 +- internal/cmd/common/flags.go | 244 +++++++++++ internal/cmd/common/flags_test.go | 405 ++++++++++++++++++ internal/cmd/gencli/input.go | 19 + internal/cmd/gencli/templates.go | 30 +- internal/host/plugin/repository_host_set.go | 16 +- .../host/plugin/repository_host_set_test.go | 16 +- .../api/resources/hostsets/v1/host_set.proto | 2 +- .../handlers/targets/target_service_test.go | 4 +- .../api/resources/hostsets/host_set.pb.go | 25 +- 16 files changed, 1166 insertions(+), 36 deletions(-) create mode 100644 internal/cmd/commands/hostcatalogscmd/plugin_funcs.go create mode 100644 internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go create mode 100644 internal/cmd/common/flags_test.go diff --git a/api/hostsets/option.gen.go b/api/hostsets/option.gen.go index 317b79b83..eafe5b7fa 100644 --- a/api/hostsets/option.gen.go +++ b/api/hostsets/option.gen.go @@ -74,6 +74,18 @@ func WithFilter(filter string) Option { } } +func WithAttributes(inAttributes map[string]interface{}) Option { + return func(o *options) { + o.postMap["attributes"] = inAttributes + } +} + +func DefaultAttributes() Option { + return func(o *options) { + o.postMap["attributes"] = nil + } +} + func WithDescription(inDescription string) Option { return func(o *options) { o.postMap["description"] = inDescription diff --git a/internal/cmd/base/base.go b/internal/cmd/base/base.go index 0738b5ec6..826c7e2f2 100644 --- a/internal/cmd/base/base.go +++ b/internal/cmd/base/base.go @@ -83,6 +83,10 @@ type Command struct { FlagRecursive bool FlagFilter string + // Attribute values + FlagAttributes string + FlagAttrs []CombinedSliceFlagValue + client *api.Client } diff --git a/internal/cmd/base/flags.go b/internal/cmd/base/flags.go index 63efaf7ad..fef530e7e 100644 --- a/internal/cmd/base/flags.go +++ b/internal/cmd/base/flags.go @@ -4,11 +4,14 @@ import ( "flag" "fmt" "os" + "regexp" "sort" "strconv" "strings" "time" + "github.com/hashicorp/go-secure-stdlib/parseutil" + "github.com/kr/pretty" "github.com/posener/complete" ) @@ -748,3 +751,118 @@ func (f *FlagSet) Var(value flag.Value, name, usage string) { f.mainSet.Var(value, name, usage) f.flagSet.Var(value, name, usage) } + +// CombinationSliceVar uses a wrapped value to allow storing values from +// different flags in one slice. This is useful if you need ordering to be +// maintained across flags. It does not currently support env vars. +// +// If KvSplit is set true, each value will be split on the first = into Key and +// Value parts so that validation can happen at parsing time. If you don't want +// this kind of behavior, simply combine them, or set KvSplit to false. +// +// If KeyDelimiter is non-nil (along with KvSplit being true), the string will +// be used to split the key. Otherwise, the Keys will be a single-element slice +// containing the full value. +// +// If ProtoCompat is true, the key will be validated against proto3 syntax +// requirements for identifiers. If the string is split via KeyDelimiter, each +// segment will be evaluated independently. +type CombinationSliceVar struct { + Name string + Aliases []string + Usage string + Hidden bool + Target *[]CombinedSliceFlagValue + Completion complete.Predictor + KvSplit bool + KeyDelimiter *string + ProtoCompatKey bool +} + +func (f *FlagSet) CombinationSliceVar(i *CombinationSliceVar) { + f.VarFlag(&VarFlag{ + Name: i.Name, + Aliases: i.Aliases, + Usage: i.Usage, + Value: newCombinedSliceValue(i.Name, i.Target, i.Hidden, i.KvSplit, i.KeyDelimiter, i.ProtoCompatKey), + Completion: i.Completion, + }) +} + +// CombinedSliceValue holds the raw value (as a string) and the name of the flag +// that added it. +type CombinedSliceFlagValue struct { + Name string + Keys []string + Value string +} + +type combinedSliceValue struct { + name string + hidden bool + kvSplit bool + keyDelimiter *string + protoCompatKey bool + target *[]CombinedSliceFlagValue +} + +func newCombinedSliceValue(name string, target *[]CombinedSliceFlagValue, hidden, kvSplit bool, keyDelimiter *string, protoCompatKey bool) *combinedSliceValue { + return &combinedSliceValue{ + name: name, + hidden: hidden, + kvSplit: kvSplit, + keyDelimiter: keyDelimiter, + protoCompatKey: protoCompatKey, + target: target, + } +} + +var protoIdentifierRegex = regexp.MustCompile("^[a-zA-Z][A-Za-z0-9_]*$") + +func (c *combinedSliceValue) Set(val string) error { + ret := CombinedSliceFlagValue{ + Name: c.name, + Value: strings.TrimSpace(val), + } + + if c.kvSplit { + kv := strings.SplitN(ret.Value, "=", 2) + switch len(kv) { + case 0: + case 1: + ret.Value = strings.TrimSpace(kv[0]) + default: + ret.Keys = []string{kv[0]} + if c.keyDelimiter != nil { + ret.Keys = strings.Split(kv[0], *c.keyDelimiter) + } + ret.Value = strings.TrimSpace(kv[1]) + } + } + + // Trim keys + for i, key := range ret.Keys { + ret.Keys[i] = strings.TrimSpace(key) + } + + if c.protoCompatKey { + for _, key := range ret.Keys { + if !protoIdentifierRegex.Match([]byte(key)) { + return fmt.Errorf("key segment %q is invalid", key) + } + } + } + + var err error + if ret.Value, err = parseutil.ParsePath(ret.Value); err != nil && err != parseutil.ErrNotAUrl { + return fmt.Errorf("error checking if value is a path: %w", err) + } + + *c.target = append(*c.target, ret) + return nil +} + +func (c *combinedSliceValue) Get() interface{} { return *c.target } +func (c *combinedSliceValue) String() string { return pretty.Sprint(*c.target) } +func (c *combinedSliceValue) Example() string { return "" } +func (c *combinedSliceValue) Hidden() bool { return c.hidden } diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 270e41562..8cd7d9cc0 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -512,6 +512,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "create", }, nil }, + "host-catalogs create plugin": func() (cli.Command, error) { + return &hostcatalogscmd.PluginCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, "host-catalogs update": func() (cli.Command, error) { return &hostcatalogscmd.Command{ Command: base.NewCommand(ui), @@ -524,6 +530,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "update", }, nil }, + "host-catalogs update plugin": func() (cli.Command, error) { + return &hostcatalogscmd.PluginCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, "host-sets": func() (cli.Command, error) { return &hostsetscmd.Command{ diff --git a/internal/cmd/commands/hostcatalogscmd/plugin_funcs.go b/internal/cmd/commands/hostcatalogscmd/plugin_funcs.go new file mode 100644 index 000000000..5bf4ac915 --- /dev/null +++ b/internal/cmd/commands/hostcatalogscmd/plugin_funcs.go @@ -0,0 +1,33 @@ +package hostcatalogscmd + +import ( + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func (c *PluginCommand) extraPluginHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary host-catalogs create plugin [options] [args]", + "", + " Create a plugin-type host catalog. Example:", + "", + ` $ boundary host-catalogs create plugin -scope-id p_1234567890 -name prodops -description "Plugin host-catalog for ProdOps"`, + "", + "", + }) + + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary host-catalogs update plugin [options] [args]", + "", + " Update a plugin-type host catalog given its ID. Example:", + "", + ` $ boundary host-catalogs update plugin -id hc_1234567890 -name "devops" -description "Plugin host-catalog for DevOps"`, + "", + "", + }) + } + return helpStr + c.Flags().Help() +} diff --git a/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go b/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go new file mode 100644 index 000000000..223ee3026 --- /dev/null +++ b/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go @@ -0,0 +1,260 @@ +// Code generated by "make cli"; DO NOT EDIT. +package hostcatalogscmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/hostcatalogs" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initPluginFlags() { + flagsOnce.Do(func() { + extraFlags := extraPluginActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsPluginMap[k] = append(flagsPluginMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*PluginCommand)(nil) + _ cli.CommandAutocomplete = (*PluginCommand)(nil) +) + +type PluginCommand struct { + *base.Command + + Func string + + plural string +} + +func (c *PluginCommand) AutocompleteArgs() complete.Predictor { + initPluginFlags() + return complete.PredictAnything +} + +func (c *PluginCommand) AutocompleteFlags() complete.Flags { + initPluginFlags() + return c.Flags().Completions() +} + +func (c *PluginCommand) Synopsis() string { + if extra := extraPluginSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "host catalog" + + synopsisStr = fmt.Sprintf("%s %s", "plugin-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *PluginCommand) Help() string { + initPluginFlags() + + var helpStr string + helpMap := common.HelpMap("host catalog") + + switch c.Func { + default: + + helpStr = c.extraPluginHelpFunc(helpMap) + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsPluginMap = map[string][]string{ + + "create": {"scope-id", "name", "description", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, + + "update": {"id", "name", "description", "version", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, +} + +func (c *PluginCommand) Flags() *base.FlagSets { + if len(flagsPluginMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "plugin-type host catalog", flagsPluginMap, c.Func) + + f = set.NewFlagSet("Attribute Options") + common.PopulateAttributeFlags(c.Command, f, flagsPluginMap, c.Func) + + extraPluginFlagsFunc(c, set, f) + + return set +} + +func (c *PluginCommand) Run(args []string) int { + initPluginFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + } + + c.plural = "plugin-type host catalog" + switch c.Func { + case "list": + c.plural = "plugin-type host catalogs" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsPluginMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []hostcatalogs.Option + + if strutil.StrListContains(flagsPluginMap[c.Func], "scope-id") { + switch c.Func { + case "create": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + } + } + + client, err := c.Client() + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %s", err.Error())) + return base.CommandCliError + } + hostcatalogsClient := hostcatalogs.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, hostcatalogs.DefaultName()) + default: + opts = append(opts, hostcatalogs.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, hostcatalogs.DefaultDescription()) + default: + opts = append(opts, hostcatalogs.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, hostcatalogs.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, hostcatalogs.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, hostcatalogs.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + if err := common.HandleAttributeFlags( + c.Command, + "attr", + func() { + hostcatalogs.DefaultAttributes() + }, + func(in map[string]interface{}) { + hostcatalogs.WithAttributes(in) + }); err != nil { + c.PrintCliError(fmt.Errorf("Error evaluating attribute flags to: %s", err.Error())) + return base.CommandCliError + } + + if ok := extraPluginFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + case "create": + result, err = hostcatalogsClient.Create(c.Context, "plugin", c.FlagScopeId, opts...) + + case "update": + result, err = hostcatalogsClient.Update(c.Context, c.FlagId, version, opts...) + + } + + result, err = executeExtraPluginActions(c, result, err, hostcatalogsClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomPluginActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraPluginActionsFlagsMapFunc = func() map[string][]string { return nil } + extraPluginSynopsisFunc = func(*PluginCommand) string { return "" } + extraPluginFlagsFunc = func(*PluginCommand, *base.FlagSets, *base.FlagSet) {} + extraPluginFlagsHandlingFunc = func(*PluginCommand, *base.FlagSets, *[]hostcatalogs.Option) bool { return true } + executeExtraPluginActions = func(_ *PluginCommand, inResult api.GenericResult, inErr error, _ *hostcatalogs.Client, _ uint32, _ []hostcatalogs.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomPluginActionOutput = func(*PluginCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/hostcatalogscmd/static_funcs.go b/internal/cmd/commands/hostcatalogscmd/static_funcs.go index e0a571c1d..48d5daee8 100644 --- a/internal/cmd/commands/hostcatalogscmd/static_funcs.go +++ b/internal/cmd/commands/hostcatalogscmd/static_funcs.go @@ -13,7 +13,7 @@ func (c *StaticCommand) extraStaticHelpFunc(helpMap map[string]func() string) st "", " Create a static-type host catalog. Example:", "", - ` $ boundary host-catalogs create static -name prodops -description "Static host-catalog for ProdOps"`, + ` $ boundary host-catalogs create static -scope-id p_1234567890 -name prodops -description "Static host-catalog for ProdOps"`, "", "", }) diff --git a/internal/cmd/common/flags.go b/internal/cmd/common/flags.go index 5ab75fc46..df15751d9 100644 --- a/internal/cmd/common/flags.go +++ b/internal/cmd/common/flags.go @@ -1,9 +1,16 @@ package common import ( + "bytes" + "encoding/json" + "errors" "fmt" + "regexp" + "strconv" + "strings" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/posener/complete" ) @@ -93,3 +100,240 @@ func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string, } } } + +func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[string][]string, command string) { + keyDelimiter := "." + for _, name := range flagNames[command] { + switch name { + case "attributes": + f.StringVar(&base.StringVar{ + Name: "attributes", + Target: &c.FlagAttributes, + Usage: `A JSON map value to use as the entirety of the request's attribute map. Usually this will be soured from a file via "file://" syntax.`, + }) + case "attr": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "attr", + Target: &c.FlagAttrs, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value attribute to add to the request's attribute map. The type is automatically inferred. Use -string-attr, -bool-attr, or -num-attr if the type needs to be overridden. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "string-attr": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "string-attr", + Target: &c.FlagAttrs, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value string attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "bool-attr": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "bool-attr", + Target: &c.FlagAttrs, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value bool attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "num-attr": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "num-attr", + Target: &c.FlagAttrs, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value numeric attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + } + } +} + +// From https://stackoverflow.com/a/13340826, modified to remove exponents +var jsonNumberRegex = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?$`) + +// HandleAttributeFlags takes in a command and a func to call for default (that +// is, set to nil) and non-default values. Suffix can be used to allow this +// logic to be used for various needs, e.g. -attr vs -secret. +func HandleAttributeFlags(c *base.Command, suffix string, defaultFunc func(), setFunc func(map[string]interface{})) error { + // If we were given -attributes, use that as-is + switch c.FlagAttributes { + case "": + // Nothing, continue on + case "null": + defaultFunc() + return nil + default: + parsedString, err := parseutil.ParsePath(c.FlagAttributes) + if err != nil && !errors.Is(err, parseutil.ErrNotAUrl) { + return fmt.Errorf("error parsing attributes flag as a URL: %w", err) + } + // We should be able to parse the string as a JSON object + var setMap map[string]interface{} + if err := json.Unmarshal([]byte(parsedString), &setMap); err != nil { + return fmt.Errorf("error parsing attributes flag as JSON: %w", err) + } + setFunc(setMap) + return nil + } + + setMap := map[string]interface{}{} + + for _, attr := range c.FlagAttrs { + if len(attr.Keys) == 0 { + // No idea why this would happen, but skip it + continue + } + + var val interface{} + var err error + + // First, perform any needed parsing if we are given the type + switch attr.Name { + case "num-" + suffix: + // JSON treats all numbers equally, however, we will try to be a + // little better so that we don't include decimals if we don't need + // to (and don't have to worry about precision if not necessary) + if strings.Contains(attr.Value, ".") { + val, err = strconv.ParseFloat(attr.Value, 64) + if err != nil { + return fmt.Errorf("error parsing attribute %q as a float: %w", attr.Value, err) + } + } else { + val, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return fmt.Errorf("error parsing attribute %q as an integer: %w", attr.Value, err) + } + } + + case "string-" + suffix: + val = strings.Trim(attr.Value, `"`) + + case "bool-" + suffix: + switch attr.Value { + case "true": + val = true + case "false": + val = false + default: + return fmt.Errorf("error parsing attribute %q as a bool", attr.Value) + } + + case suffix: + // In this case, use heuristics to just do the right thing the vast + // majority of the time + switch { + case attr.Value == "null": // Explicit null, we want to set to a null value to clear it + val = nil + + case attr.Value == "true": // bool true + val = true + + case attr.Value == "false": // bool false + val = false + + case strings.HasPrefix(attr.Value, `"`): // explicitly quoted string + val = strings.Trim(attr.Value, `"`) + + case jsonNumberRegex.Match([]byte(strings.Trim(attr.Value, `"`))): // number + // Same logic as above + if strings.Contains(attr.Value, ".") { + val, err = strconv.ParseFloat(attr.Value, 64) + if err != nil { + return fmt.Errorf("error parsing attribute %q as a float: %w", attr.Value, err) + } + } else { + val, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return fmt.Errorf("error parsing attribute %q as an integer: %w", attr.Value, err) + } + } + + case strings.HasPrefix(attr.Value, "["): // serialized JSON array + var s []interface{} + u := json.NewDecoder(bytes.NewBufferString(attr.Value)) + u.UseNumber() + if err := u.Decode(&s); err != nil { + return fmt.Errorf("error parsing attribute %q as a json array: %w", attr.Value, err) + } + val = s + + case strings.HasPrefix(attr.Value, "{"): // serialized JSON map + var m map[string]interface{} + u := json.NewDecoder(bytes.NewBufferString(attr.Value)) + u.UseNumber() + if err := u.Decode(&m); err != nil { + return fmt.Errorf("error parsing attribute %q as a json map: %w", attr.Value, err) + } + val = m + + default: + // Default is to treat as a string value + val = attr.Value + } + default: + return fmt.Errorf("unknown attribute flag %q", attr.Name) + } + + // Now we have to insert it in the right position in the attribute map + currMap := setMap + for i, segment := range attr.Keys { + if segment == "" { + return fmt.Errorf("key segment %q for value %q is empty", segment, attr.Value) + } + + switch { + case i == len(attr.Keys)-1: + // If we get an explicit "null" override whatever is currently + // there + if val == nil { + currMap[segment] = nil + break + } + // We're at the last hop, do the actual insertion + switch t := currMap[segment].(type) { + case nil: + // Nothing currently exists + currMap[segment] = val + + case []interface{}: + // It's already a slice, so just append + currMap[segment] = append(t, val) + + default: + // It's not a slice, so create a new slice with the + // exisitng and new values + currMap[segment] = []interface{}{t, val} + } + + default: + // We need to keep traversing + switch t := currMap[segment].(type) { + case nil: + // We haven't hit this segment before, so create a new + // object leading off of it and set it to current + newMap := map[string]interface{}{} + currMap[segment] = newMap + currMap = newMap + + case map[string]interface{}: + // We've seen this before and already have a map so just set + // that as our new location + currMap = t + + default: + // We should only ever be seeing maps if we're not at the + // final location + return fmt.Errorf("unexpected type for key segment %q: %T", segment, t) + } + } + } + } + + if len(setMap) > 0 { + setFunc(setMap) + } + return nil +} diff --git a/internal/cmd/common/flags_test.go b/internal/cmd/common/flags_test.go new file mode 100644 index 000000000..b4e3bd4d6 --- /dev/null +++ b/internal/cmd/common/flags_test.go @@ -0,0 +1,405 @@ +package common + +import ( + "encoding/json" + "testing" + + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestPopulateAttrFlags tests common patterns we'll actually be using. Note +// that this is not an exhaustive test of the full CombinationSliceVar +// functionality, it's a bit higher level test based on what we'll actually need +// and what we'll actually have set. +func TestPopulateAttrFlags(t *testing.T) { + tests := []struct { + name string + args []string + expected []base.CombinedSliceFlagValue + expectedErr string + }{ + { + name: "strings-only", + args: []string{"-string-attr", "foo=bar", "-string-attr", `bar="baz"`}, + expected: []base.CombinedSliceFlagValue{ + { + Name: "string-attr", + Keys: []string{"foo"}, + Value: "bar", + }, + { + Name: "string-attr", + Keys: []string{"bar"}, + Value: `"baz"`, + }, + }, + }, + { + name: "nums-only", + args: []string{"-num-attr", "foo=-1.2", "-num-attr", "bar=5"}, + expected: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo"}, + Value: "-1.2", + }, + { + Name: "num-attr", + Keys: []string{"bar"}, + Value: "5", + }, + }, + }, + { + name: "bools-only", + args: []string{"-bool-attr", "foo=true", "-bool-attr", "bar=false"}, + expected: []base.CombinedSliceFlagValue{ + { + Name: "bool-attr", + Keys: []string{"foo"}, + Value: "true", + }, + { + Name: "bool-attr", + Keys: []string{"bar"}, + Value: "false", + }, + }, + }, + { + name: "mixed", + args: []string{"-num-attr", "foo=9820", "-string-attr", "bar=9820", "-attr", "baz=9820"}, + expected: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo"}, + Value: "9820", + }, + { + Name: "string-attr", + Keys: []string{"bar"}, + Value: "9820", + }, + { + Name: "attr", + Keys: []string{"baz"}, + Value: "9820", + }, + }, + }, + { + name: "mixed-segments", + args: []string{"-num-attr", "foo.bar.baz=9820", "-string-attr", "bar.baz.foo=9820", "-attr", "baz.foo.bar=9820"}, + expected: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo", "bar", "baz"}, + Value: "9820", + }, + { + Name: "string-attr", + Keys: []string{"bar", "baz", "foo"}, + Value: "9820", + }, + { + Name: "attr", + Keys: []string{"baz", "foo", "bar"}, + Value: "9820", + }, + }, + }, + { + name: "bad-key-name", + args: []string{"-num-attr", "fo-oo=5"}, + expectedErr: "invalid value", + }, + { + name: "bad-key-name-in-segment", + args: []string{"-num-attr", "fo.oo-o.o=5"}, + expectedErr: "invalid value", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // Note: we do the setup on each run to make sure we aren't carrying + // state over; just like in the real CLI where each run would have + // pristine state. + c := new(base.Command) + flagSet := c.FlagSet(base.FlagSetNone) + f := flagSet.NewFlagSet("Attribute Options") + cmd := "create" + flagNames := map[string][]string{ + cmd: { + "attributes", + "attr", + "string-attr", + "bool-attr", + "num-attr", + }, + } + + PopulateAttributeFlags(c, f, flagNames, cmd) + err := flagSet.Parse(tt.args) + if tt.expectedErr != "" { + require.Error(err) + assert.Contains(err.Error(), tt.expectedErr) + return + } + require.NoError(err) + assert.Equal(tt.expected, c.FlagAttrs) + }) + } +} + +// TestHandleAttributeFlags tests the function that parses types based on +// incoming data. It assumes we're coming in with CombinedSliceFlagValues and +// validates what comes out -- whether nil func was called or the map function +// was called (and its contents). +func TestHandleAttributeFlags(t *testing.T) { + tests := []struct { + name string + args []base.CombinedSliceFlagValue + expectedMap map[string]interface{} + expectedErr string + }{ + { + name: "strings-only", + args: []base.CombinedSliceFlagValue{ + { + Name: "string-attr", + Keys: []string{"foo"}, + Value: "bar", + }, + { + Name: "string-attr", + Keys: []string{"bar"}, + Value: `"baz"`, + }, + }, + expectedMap: map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + }, + { + name: "nums-only", + args: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo"}, + Value: "-1.2", + }, + { + Name: "num-attr", + Keys: []string{"bar"}, + Value: "5", + }, + }, + expectedMap: map[string]interface{}{ + "foo": float64(-1.2), + "bar": int64(5), + }, + }, + { + name: "bad-float-num", + args: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo"}, + Value: "-15d.2", + }, + }, + expectedErr: "as a float", + }, + { + name: "bad-int-num", + args: []base.CombinedSliceFlagValue{ + { + Name: "num-attr", + Keys: []string{"foo"}, + Value: "-15d3", + }, + }, + expectedErr: "as an int", + }, + { + name: "bools-only", + args: []base.CombinedSliceFlagValue{ + { + Name: "bool-attr", + Keys: []string{"foo"}, + Value: "true", + }, + { + Name: "bool-attr", + Keys: []string{"bar"}, + Value: "false", + }, + }, + expectedMap: map[string]interface{}{ + "foo": true, + "bar": false, + }, + }, + { + name: "bad-bool", + args: []base.CombinedSliceFlagValue{ + { + Name: "bool-attr", + Keys: []string{"foo"}, + Value: "t", + }, + }, + expectedErr: "as a bool", + }, + { + name: "attr-only", + args: []base.CombinedSliceFlagValue{ + { + Name: "attr", + Keys: []string{"b1"}, + Value: "true", + }, + { + Name: "attr", + Keys: []string{"b2"}, + Value: "false", + }, + { + Name: "attr", + Keys: []string{"s1"}, + Value: "scoopde", + }, + { + Name: "attr", + Keys: []string{"s2"}, + Value: `"woop"`, + }, + { + Name: "attr", + Keys: []string{"n1"}, + Value: "-1.2", + }, + { + Name: "attr", + Keys: []string{"n2"}, + Value: "5", + }, + { + Name: "attr", + Keys: []string{"a"}, + Value: `["foo", 1.5, true, ["bar"], {"hip": "hop"}]`, + }, + { + Name: "attr", + Keys: []string{"nil"}, + Value: "null", + }, + { + Name: "attr", + Keys: []string{"m"}, + Value: `{"b": true, "n": 6, "s": "scoopde", "a": ["bar"], "m": {"hip": "hop"}}`, + }, + }, + expectedMap: map[string]interface{}{ + "b1": true, + "b2": false, + "s1": "scoopde", + "s2": "woop", + "n1": float64(-1.2), + "n2": int64(5), + "a": []interface{}{ + "foo", + json.Number("1.5"), + true, + []interface{}{"bar"}, + map[string]interface{}{"hip": "hop"}, + }, + "m": map[string]interface{}{ + "b": true, + "n": json.Number("6"), + "s": "scoopde", + "a": []interface{}{"bar"}, + "m": map[string]interface{}{"hip": "hop"}, + }, + "nil": nil, + }, + }, + { + name: "map-array-structure", + args: []base.CombinedSliceFlagValue{ + { + Name: "attr", + Keys: []string{"bools"}, + Value: "true", + }, + { + Name: "attr", + Keys: []string{"bools"}, + Value: "false", + }, + { + Name: "attr", + Keys: []string{"strings", "s1"}, + Value: "scoopde", + }, + { + Name: "attr", + Keys: []string{"strings", "s2"}, + Value: `"woop"`, + }, + { + Name: "attr", + Keys: []string{"numbers", "reps"}, + Value: "-1.2", + }, + { + Name: "attr", + Keys: []string{"numbers", "reps"}, + Value: "5", + }, + { + Name: "attr", + Keys: []string{"strings", "s2"}, // This will overwrite above! + Value: "null", + }, + }, + expectedMap: map[string]interface{}{ + "bools": []interface{}{true, false}, + "strings": map[string]interface{}{ + "s1": "scoopde", + "s2": nil, + }, + "numbers": map[string]interface{}{ + "reps": []interface{}{float64(-1.2), int64(5)}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // Note: we do the setup on each run to make sure we aren't carrying + // state over; just like in the real CLI where each run would have + // pristine state. + c := new(base.Command) + c.FlagAttrs = tt.args + var outMap map[string]interface{} + + err := HandleAttributeFlags(c, "attr", func() {}, func(in map[string]interface{}) { outMap = in }) + if tt.expectedErr != "" { + require.Error(err) + assert.Contains(err.Error(), tt.expectedErr) + return + } + + require.NoError(err) + assert.Equal(tt.expectedMap, outMap) + }) + } +} diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 8a044fecc..e88fdefd1 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -68,6 +68,10 @@ type cmdInfo struct { // This allows the flags to be defined differently from the the attribute // names in the API. PrefixAttributeFieldErrorsWithSubactionPrefix bool + + // HasGenericAttributes controls whether to generate flags for -attributes, + // -attr, -string-attr, etc. + HasGenericAttributes bool } var inputStructs = map[string][]*cmdInfo{ @@ -254,6 +258,21 @@ var inputStructs = map[string][]*cmdInfo{ VersionedActions: []string{"update"}, NeedsSubtypeInCreate: true, }, + { + ResourceType: resource.HostCatalog.String(), + Pkg: "hostcatalogs", + StdActions: []string{"create", "update"}, + SubActionPrefix: "plugin", + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + HasDescription: true, + Container: "Scope", + VersionedActions: []string{"update"}, + NeedsSubtypeInCreate: true, + HasGenericAttributes: true, + }, }, "hostsets": { { diff --git a/internal/cmd/gencli/templates.go b/internal/cmd/gencli/templates.go index a6851506e..22d591a02 100644 --- a/internal/cmd/gencli/templates.go +++ b/internal/cmd/gencli/templates.go @@ -169,15 +169,16 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Help() string { } var flags{{ camelCase .SubActionPrefix }}Map = map[string][]string{ - {{ range $i, $action := .StdActions }} + {{ with $attrFlags := ", \"attributes\", \"attr\", \"string-attr\", \"bool-attr\", \"num-attr\"" }} + {{ range $i, $action := $input.StdActions }} {{ if eq $action "create" }} - "create": { "{{ kebabCase $input.Container }}-id", "name", "description" }, + "create": { "{{ kebabCase $input.Container }}-id", "name", "description" {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} }, {{ end }} {{ if eq $action "read" }} "read": {"id"}, {{ end }} {{ if eq $action "update" }} - "update": {"id", "name", "description" {{ if hasAction $input.VersionedActions "update" }}, "version" {{ end }} }, + "update": {"id", "name", "description" {{ if hasAction $input.VersionedActions "update" }}, "version" {{ end }} {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} }, {{ end }} {{ if eq $action "delete" }} "delete": {"id"}, @@ -186,6 +187,7 @@ var flags{{ camelCase .SubActionPrefix }}Map = map[string][]string{ "list": { "{{ kebabCase $input.Container }}-id", "filter" {{ if (eq $input.Container "Scope") }}, "recursive"{{ end }} }, {{ end }} {{ end }} + {{ end }} } func (c *{{ camelCase .SubActionPrefix }}Command) Flags() *base.FlagSets { @@ -197,6 +199,11 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Flags() *base.FlagSets { f := set.NewFlagSet("Command Options") common.PopulateCommonFlags(c.Command, f, "{{ if .SubActionPrefix }}{{ .SubActionPrefix }}-type {{ end }}{{ lowerSpaceCase .ResourceType }}", flags{{ camelCase .SubActionPrefix }}Map, c.Func) + {{ if .HasGenericAttributes }} + f = set.NewFlagSet("Attribute Options") + common.PopulateAttributeFlags(c.Command, f, flags{{ camelCase .SubActionPrefix }}Map, c.Func) + {{ end }} + extra{{ camelCase .SubActionPrefix }}FlagsFunc(c, set, f) return set @@ -323,7 +330,22 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { } {{ end }} - if ok := extra{{ camelCase .SubActionPrefix }}FlagsHandlingFunc(c, f, &opts); !ok { + {{ if .HasGenericAttributes }} + if err := common.HandleAttributeFlags( + c.Command, + "attr", + func() { + {{ .Pkg }}.DefaultAttributes() + }, + func(in map[string]interface{}) { + {{ .Pkg }}.WithAttributes(in) + }); err != nil { + c.PrintCliError(fmt.Errorf("Error evaluating attribute flags to: %s", err.Error())) + return base.CommandCliError + } + {{ end }} + + if ok := extra{{ camelCase .SubActionPrefix }}FlagsHandlingFunc(c, f, &opts, ); !ok { return base.CommandUserError } diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 74c798a72..3abda7fc7 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -373,14 +373,14 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En type setInfo struct { preferredEndpoint endpoint.Option - plgSet *pb.HostSet + plgSet *pb.HostSet } - type catalogInfo struct{ - publicId string - plg plgpb.HostPluginServiceServer - setInfos map[string]*setInfo - plgCat *hcpb.HostCatalog + type catalogInfo struct { + publicId string + plg plgpb.HostPluginServiceServer + setInfos map[string]*setInfo + plgCat *hcpb.HostCatalog persisted *plgpb.HostCatalogPersisted } @@ -458,10 +458,10 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En sets = append(sets, si.plgSet) } - resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ + resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ Catalog: ci.plgCat, Sets: sets, - //Persisted: ci.persisted, + // Persisted: ci.persisted, }) if err != nil { return nil, errors.Wrap(ctx, err, op) diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index d875ca999..3327ab13b 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -389,7 +389,7 @@ func TestRepository_Endpoints(t *testing.T) { } return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ { - SetIds: setIds, + SetIds: setIds, ExternalId: "test", IpAddresses: []string{"10.0.0.5", "192.168.0.5"}, DnsNames: nil, @@ -407,7 +407,7 @@ func TestRepository_Endpoints(t *testing.T) { tests := []struct { name string - setIds []string + setIds []string want []*host.Endpoint wantIsErr errors.Code }{ @@ -416,7 +416,7 @@ func TestRepository_Endpoints(t *testing.T) { wantIsErr: errors.InvalidParameter, }, { - name: "with-set10", + name: "with-set10", setIds: []string{hostSet10.GetPublicId()}, want: []*host.Endpoint{ { @@ -431,7 +431,7 @@ func TestRepository_Endpoints(t *testing.T) { }, }, { - name: "with-different-set", + name: "with-different-set", setIds: []string{hostSet192.GetPublicId()}, want: []*host.Endpoint{ { @@ -446,14 +446,14 @@ func TestRepository_Endpoints(t *testing.T) { }, }, { - name: "with-all-addresses-filtered-set", + name: "with-all-addresses-filtered-set", setIds: []string{hostSet100.GetPublicId()}, - want: nil, + want: nil, }, { - name: "with-no-hosts-from-plugin", + name: "with-no-hosts-from-plugin", setIds: []string{hostlessSet.GetPublicId()}, - want: nil, + want: nil, }, } diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index 2fcdba617..cb28046e0 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -52,7 +52,7 @@ message HostSet { repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true]; // The attributes that are applicable for the specific Host Set type. - google.protobuf.Struct attributes = 110; + google.protobuf.Struct attributes = 110 [(custom_options.v1.generate_sdk_option) = true]; // Output only. The available actions on this resource for this user. repeated string authorized_actions = 300 [json_name="authorized_actions"]; diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 7b6e19936..8595a420c 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -2599,12 +2599,12 @@ func TestAuthorizeSession(t *testing.T) { } return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ { - SetIds: setIds, + SetIds: setIds, ExternalId: "test", IpAddresses: []string{"10.0.0.1", "192.168.0.1"}, }, { - SetIds: setIds, + SetIds: setIds, ExternalId: "test2", IpAddresses: []string{"10.1.1.1", "192.168.1.1"}, }, diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index 787136971..ff4bcb274 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -217,7 +217,7 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe9, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, @@ -257,19 +257,20 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, - 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, + 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, + 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 7d71618df02fab4a83369fa45434143953c3877d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 28 Sep 2021 09:40:12 -0400 Subject: [PATCH 25/71] Add more host-catalog and host-set CLI support (#1567) Add more host-catalog and host-set CLI support Co-authored-by: Todd --- api/hostcatalogs/option.gen.go | 19 ++ globals/fields.go | 1 + internal/api/genapi/input.go | 21 +- internal/api/genapi/templates.go | 10 +- internal/cmd/base/base.go | 6 + internal/cmd/base/dev.go | 16 +- internal/cmd/base/format.go | 12 ++ internal/cmd/base/initial_resources.go | 48 +++++ internal/cmd/base/option.go | 16 +- internal/cmd/base/servers.go | 4 + internal/cmd/commands/dev/dev.go | 71 ++++--- .../cmd/commands/hostcatalogscmd/funcs.go | 32 ++- .../plugin_hostcatalogs.gen.go | 39 +++- internal/cmd/commands/hostsetscmd/funcs.go | 24 ++- .../hostsetscmd/plugin_hostsets.gen.go | 22 +- internal/cmd/common/flags.go | 154 ++++++++++---- internal/cmd/common/flags_test.go | 92 +++++---- internal/cmd/gencli/input.go | 34 ++-- internal/cmd/gencli/templates.go | 47 ++++- internal/gen/controller.swagger.json | 2 +- .../api/services/host_catalog_service.pb.go | 192 +++++++++--------- internal/host/plugin/host_catalog.go | 8 +- internal/host/plugin/repository.go | 5 +- .../host/plugin/repository_host_catalog.go | 4 +- .../plugin/repository_host_catalog_test.go | 4 +- internal/plugin/host/options.go | 12 +- internal/plugin/host/options_test.go | 12 +- internal/plugin/host/plugin.go | 2 +- internal/plugin/host/plugin_test.go | 2 - internal/plugin/host/repository.go | 2 +- internal/plugin/host/repository_plugin.go | 17 +- .../hostcatalogs/v1/host_catalog.proto | 2 +- .../services/v1/host_catalog_service.proto | 4 +- internal/servers/controller/controller.go | 8 +- .../host_catalogs/host_catalog_service.go | 84 +++++--- .../host_catalog_service_test.go | 56 ++--- .../resources/hostcatalogs/host_catalog.pb.go | 125 ++++++------ 37 files changed, 803 insertions(+), 406 deletions(-) diff --git a/api/hostcatalogs/option.gen.go b/api/hostcatalogs/option.gen.go index f0d29cee6..71daabb19 100644 --- a/api/hostcatalogs/option.gen.go +++ b/api/hostcatalogs/option.gen.go @@ -1,6 +1,7 @@ package hostcatalogs import ( + "fmt" "strconv" "strings" @@ -123,8 +124,26 @@ func DefaultName() Option { } } +func WithPluginId(inPluginId string) Option { + return func(o *options) { + o.postMap["plugin_id"] = inPluginId + } +} + +func WithPluginName(inPluginName string) Option { + return func(o *options) { + o.queryMap["plugin_name"] = fmt.Sprintf("%v", inPluginName) + } +} + func WithSecrets(inSecrets map[string]interface{}) Option { return func(o *options) { o.postMap["secrets"] = inSecrets } } + +func DefaultSecrets() Option { + return func(o *options) { + o.postMap["secrets"] = nil + } +} diff --git a/globals/fields.go b/globals/fields.go index 0ddaacf44..fe6738718 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -61,4 +61,5 @@ const ( PreferredEndpointsField = "preferred_endpoints" PluginIdField = "plugin_id" PluginField = "plugin" + PluginNameField = "plugin_name" ) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 938699ea1..46341352a 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -95,13 +95,13 @@ type structInfo struct { // listing recursiveListing bool - // extraOptions allows specifying extra options that will be created for a + // extraFields allows specifying extra options that will be created for a // given type, e.g. arguments only valid for one call or purpose and not // conveyed within the item itself - extraOptions []fieldInfo + extraFields []fieldInfo // fieldOverrides allows overriding some field behavior without making them - // "new" fields like with extraOptions + // "new" fields like with extraFields fieldOverrides []fieldInfo // createResponseTypes controls for which structs response types are created @@ -156,7 +156,7 @@ var inputStructs = []*structInfo{ listTemplate, }, pluralResourceName: "scopes", - extraOptions: []fieldInfo{ + extraFields: []fieldInfo{ { Name: "SkipAdminRoleCreation", ProtoName: "skip_admin_role_creation", @@ -508,9 +508,18 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, + extraFields: []fieldInfo{ + { + Name: "PluginName", + ProtoName: "plugin_name", + FieldType: "string", + SkipDefault: true, + Query: true, + }, + }, fieldOverrides: []fieldInfo{ { - Name: "Secrets", + Name: "PluginId", SkipDefault: true, }, }, @@ -632,7 +641,7 @@ var inputStructs = []*structInfo{ "CredentialLibraries": {}, "CredentialSources": {}, }, - extraOptions: []fieldInfo{ + extraFields: []fieldInfo{ { Name: "HostId", ProtoName: "host_id", diff --git a/internal/api/genapi/templates.go b/internal/api/genapi/templates.go index 8bfc3c409..5b5386c99 100644 --- a/internal/api/genapi/templates.go +++ b/internal/api/genapi/templates.go @@ -44,7 +44,7 @@ type templateInput struct { ResourcePath string ParentTypeName string SliceSubtypes map[string]sliceSubtypeInfo - ExtraOptions []fieldInfo + ExtraFields []fieldInfo VersionEnabled bool TypeOnCreate bool CreateResponseTypes bool @@ -63,7 +63,7 @@ func fillTemplates() { Fields: in.generatedStructure.fields, PluralResourceName: in.pluralResourceName, ParentTypeName: in.parentTypeName, - ExtraOptions: in.extraOptions, + ExtraFields: in.extraFields, VersionEnabled: in.versionEnabled, TypeOnCreate: in.typeOnCreate, CreateResponseTypes: in.createResponseTypes, @@ -129,13 +129,13 @@ func fillTemplates() { } optionsMap[input.Package] = optionMap } - // Add in extra defined options - if len(in.extraOptions) > 0 { + // Add in extra defined fields + if len(in.extraFields) > 0 { optionMap := optionsMap[input.Package] if optionMap == nil { optionMap = map[string]fieldInfo{} } - for _, val := range in.extraOptions { + for _, val := range in.extraFields { optionMap[val.Name] = val } optionsMap[input.Package] = optionMap diff --git a/internal/cmd/base/base.go b/internal/cmd/base/base.go index 826c7e2f2..4c8205bf7 100644 --- a/internal/cmd/base/base.go +++ b/internal/cmd/base/base.go @@ -73,6 +73,8 @@ type Command struct { FlagScopeId string FlagScopeName string + FlagPluginId string + FlagPluginName string FlagId string FlagName string FlagDescription string @@ -87,6 +89,10 @@ type Command struct { FlagAttributes string FlagAttrs []CombinedSliceFlagValue + // Secret values + FlagSecrets string + FlagScrts []CombinedSliceFlagValue + client *api.Client } diff --git a/internal/cmd/base/dev.go b/internal/cmd/base/dev.go index 90db26906..5ab9a3ec1 100644 --- a/internal/cmd/base/dev.go +++ b/internal/cmd/base/dev.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/testing/dbtest" capoidc "github.com/hashicorp/cap/oidc" @@ -100,8 +101,6 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error { return err } - b.Database.LogMode(true) - if err := b.CreateGlobalKmsKeys(ctx); err != nil { if c != nil { err = multierror.Append(err, c()) @@ -144,6 +143,19 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error { return err } + if opts.withHostPlugin != nil { + pluginId, plg := opts.withHostPlugin() + b.DevLoopbackHostPluginId = pluginId + opts := []hostplugin.Option{ + hostplugin.WithName("loopback"), + hostplugin.WithDescription("Provides an initial loopback host plugin in Boundary"), + hostplugin.WithPublicId(b.DevLoopbackHostPluginId), + } + if _, err = b.CreateHostPlugin(ctx, plg, opts...); err != nil { + return err + } + } + if opts.withSkipHostResourcesCreation { // now that we have passed all the error cases, reset c to be a noop so the // defer doesn't do anything. diff --git a/internal/cmd/base/format.go b/internal/cmd/base/format.go index 55ff54b9d..ea269680b 100644 --- a/internal/cmd/base/format.go +++ b/internal/cmd/base/format.go @@ -10,6 +10,7 @@ import ( "unicode/utf8" "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/plugins" "github.com/hashicorp/boundary/api/scopes" "github.com/mitchellh/cli" "github.com/mitchellh/go-wordwrap" @@ -34,6 +35,17 @@ func ScopeInfoForOutput(scp *scopes.ScopeInfo, maxLength int) string { return WrapMap(4, maxLength, vals) } +func PluginInfoForOutput(plg *plugins.PluginInfo, maxLength int) string { + if plg == nil { + return " " + } + vals := map[string]interface{}{ + "ID": plg.Id, + "Name": plg.Name, + } + return WrapMap(4, maxLength, vals) +} + func MaxAttributesLength(nonAttributesMap, attributesMap map[string]interface{}, keySubstMap map[string]string) int { // We always print a scope ID and in some cases this particular key ends up // being the longest key, so start with it as a baseline. It's always diff --git a/internal/cmd/base/initial_resources.go b/internal/cmd/base/initial_resources.go index ecf2e34f6..bc7080b8c 100644 --- a/internal/cmd/base/initial_resources.go +++ b/internal/cmd/base/initial_resources.go @@ -10,9 +10,11 @@ import ( "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/types/scope" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/go-secure-stdlib/base62" ) @@ -584,3 +586,49 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) return tt, nil } + +func (b *Server) CreateHostPlugin(ctx context.Context, plg plgpb.HostPluginServiceServer, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { + rw := db.New(b.Database) + + kmsRepo, err := kms.NewRepository(rw, rw) + if err != nil { + return nil, fmt.Errorf("error creating kms repository: %w", err) + } + kmsCache, err := kms.NewKms(kmsRepo) + if err != nil { + return nil, fmt.Errorf("error creating kms cache: %w", err) + } + if err := kmsCache.AddExternalWrappers( + kms.WithRootWrapper(b.RootKms), + ); err != nil { + return nil, fmt.Errorf("error adding config keys to kms: %w", err) + } + + cancelCtx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { + select { + case <-b.ShutdownCh: + cancel() + case <-cancelCtx.Done(): + } + }() + + hpRepo, err := hostplugin.NewRepository(rw, rw, kmsCache) + if err != nil { + return nil, fmt.Errorf("error creating host plugin repository: %w", err) + } + + plugin := hostplugin.NewPlugin(opt...) + plugin, err = hpRepo.CreatePlugin(cancelCtx, plugin, opt...) + if err != nil { + return nil, fmt.Errorf("error creating host plugin: %w", err) + } + + if b.HostPlugins == nil { + b.HostPlugins = make(map[string]plgpb.HostPluginServiceServer) + } + b.HostPlugins[plugin.GetPublicId()] = plg + + return plugin, nil +} diff --git a/internal/cmd/base/option.go b/internal/cmd/base/option.go index f528d6e28..96a164739 100644 --- a/internal/cmd/base/option.go +++ b/internal/cmd/base/option.go @@ -1,6 +1,9 @@ package base -import "github.com/hashicorp/boundary/internal/observability/event" +import ( + "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/boundary/sdk/pbs/plugin" +) // getOpts - iterate the inbound Options and return a struct. func getOpts(opt ...Option) Options { @@ -33,6 +36,7 @@ type Options struct { withEventFlags *EventFlags withAttributeFieldPrefix string withStatusCode int + withHostPlugin func() (string, plugin.HostPluginServiceServer) } func getDefaultOptions() Options { @@ -158,3 +162,13 @@ func WithDatabaseTemplate(template string) Option { o.withDatabaseTemplate = template } } + +// WithHostPlugin allows specifying a plugin ID and implementation to create at +// startup +func WithHostPlugin(pluginId string, plg plugin.HostPluginServiceServer) Option { + return func(o *Options) { + o.withHostPlugin = func() (string, plugin.HostPluginServiceServer) { + return pluginId, plg + } + } +} diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 9f8309bdf..642f5bb0f 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/types/scope" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/boundary/version" "github.com/hashicorp/go-hclog" wrapping "github.com/hashicorp/go-kms-wrapping" @@ -112,6 +113,9 @@ type Server struct { DevTargetDefaultPort int DevTargetSessionMaxSeconds int DevTargetSessionConnectionLimit int + DevLoopbackHostPluginId string + + HostPlugins map[string]plgpb.HostPluginServiceServer DevOidcSetup oidcSetup diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 6cb3934f6..662786b7f 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/intglobals" @@ -47,36 +48,37 @@ type Command struct { controller *controller.Controller worker *worker.Worker - flagLogLevel string - flagLogFormat string - flagCombineLogs bool - flagLoginName string - flagPassword string - flagUnprivilegedLoginName string - flagUnprivilegedPassword string - flagIdSuffix string - flagHostAddress string - flagTargetDefaultPort int - flagTargetSessionMaxSeconds int - flagTargetSessionConnectionLimit int - flagControllerAPIListenAddr string - flagControllerClusterListenAddr string - flagControllerPublicClusterAddr string - flagControllerOnly bool - flagWorkerAuthKey string - flagWorkerProxyListenAddr string - flagWorkerPublicAddr string - flagPassthroughDirectory string - flagRecoveryKey string - flagDatabaseUrl string - flagContainerImage string - flagDisableDatabaseDestruction bool - flagEventFormat string - flagAudit string - flagObservations string - flagSysEvents string - flagEveryEventAllowFilters []string - flagEveryEventDenyFilters []string + flagLogLevel string + flagLogFormat string + flagCombineLogs bool + flagLoginName string + flagPassword string + flagUnprivilegedLoginName string + flagUnprivilegedPassword string + flagIdSuffix string + flagHostAddress string + flagTargetDefaultPort int + flagTargetSessionMaxSeconds int + flagTargetSessionConnectionLimit int + flagControllerAPIListenAddr string + flagControllerClusterListenAddr string + flagControllerPublicClusterAddr string + flagControllerOnly bool + flagWorkerAuthKey string + flagWorkerProxyListenAddr string + flagWorkerPublicAddr string + flagPassthroughDirectory string + flagRecoveryKey string + flagDatabaseUrl string + flagContainerImage string + flagDisableDatabaseDestruction bool + flagEventFormat string + flagAudit string + flagObservations string + flagSysEvents string + flagEveryEventAllowFilters []string + flagEveryEventDenyFilters []string + flagCreateLoopbackHostCatalogPlugin bool } func (c *Command) Synopsis() string { @@ -308,6 +310,12 @@ func (c *Command) Flags() *base.FlagSets { Usage: `The optional every event deny filter. May be specified multiple times.`, }) + f.BoolVar(&base.BoolVar{ + Name: "create-loopback-hostcatalog-plugin", + Target: &c.flagCreateLoopbackHostCatalogPlugin, + Hidden: true, + }) + return set } @@ -542,6 +550,9 @@ func (c *Command) Run(args []string) int { }() var opts []base.Option + if c.flagCreateLoopbackHostCatalogPlugin { + opts = append(opts, base.WithHostPlugin("pl_1234567890", plugin.NewLoopbackPlugin())) + } switch c.flagDatabaseUrl { case "": if c.flagDisableDatabaseDestruction { diff --git a/internal/cmd/commands/hostcatalogscmd/funcs.go b/internal/cmd/commands/hostcatalogscmd/funcs.go index e6022239b..273ab3d1f 100644 --- a/internal/cmd/commands/hostcatalogscmd/funcs.go +++ b/internal/cmd/commands/hostcatalogscmd/funcs.go @@ -83,6 +83,11 @@ func (c *Command) printListTable(items []*hostcatalogs.HostCatalog) string { fmt.Sprintf(" Scope ID: %s", m.ScopeId), ) } + if m.PluginId != "" { + output = append(output, + fmt.Sprintf(" Plugin ID: %s", m.PluginId), + ) + } if m.Version > 0 { output = append(output, fmt.Sprintf(" Version: %d", m.Version), @@ -135,6 +140,9 @@ func printItemTable(result api.GenericResult) string { if item.Description != "" { nonAttributeMap["Description"] = item.Description } + if item.PluginId != "" { + nonAttributeMap["Plugin ID"] = item.PluginId + } if item.Type != "" { nonAttributeMap["Type"] = item.Type } @@ -155,6 +163,22 @@ func printItemTable(result api.GenericResult) string { ) } + if item.Plugin != nil { + ret = append(ret, + "", + " Plugin:", + base.PluginInfoForOutput(item.Plugin, maxLength), + ) + } + + if len(item.Attributes) > 0 { + ret = append(ret, + "", + " Attributes:", + base.WrapMap(4, maxLength, item.Attributes), + ) + } + if len(item.AuthorizedActions) > 0 { ret = append(ret, "", @@ -180,14 +204,6 @@ func printItemTable(result api.GenericResult) string { } } - if len(item.Attributes) > 0 { - ret = append(ret, - "", - " Attributes:", - base.WrapMap(4, maxLength, item.Attributes), - ) - } - return base.WrapForHelpText(ret) } diff --git a/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go b/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go index 223ee3026..41b1fb8dd 100644 --- a/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go +++ b/internal/cmd/commands/hostcatalogscmd/plugin_hostcatalogs.gen.go @@ -77,9 +77,9 @@ func (c *PluginCommand) Help() string { var flagsPluginMap = map[string][]string{ - "create": {"scope-id", "name", "description", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, + "create": {"scope-id", "name", "description", "plugin-id", "plugin-name", "attributes", "attr", "string-attr", "bool-attr", "num-attr", "secrets", "secret", "string-secret", "bool-secret", "num-secret"}, - "update": {"id", "name", "description", "version", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, + "update": {"id", "name", "description", "version", "attributes", "attr", "string-attr", "bool-attr", "num-attr", "secrets", "secret", "string-secret", "bool-secret", "num-secret"}, } func (c *PluginCommand) Flags() *base.FlagSets { @@ -94,6 +94,9 @@ func (c *PluginCommand) Flags() *base.FlagSets { f = set.NewFlagSet("Attribute Options") common.PopulateAttributeFlags(c.Command, f, flagsPluginMap, c.Func) + f = set.NewFlagSet("Secrets Options") + common.PopulateSecretFlags(c.Command, f, flagsPluginMap, c.Func) + extraPluginFlagsFunc(c, set, f) return set @@ -169,6 +172,17 @@ func (c *PluginCommand) Run(args []string) int { opts = append(opts, hostcatalogs.WithFilter(c.FlagFilter)) } + switch c.FlagPluginId { + case "": + default: + opts = append(opts, hostcatalogs.WithPluginId(c.FlagPluginId)) + } + switch c.FlagPluginName { + case "": + default: + opts = append(opts, hostcatalogs.WithPluginName(c.FlagPluginName)) + } + var version uint32 switch c.Func { @@ -184,16 +198,33 @@ func (c *PluginCommand) Run(args []string) int { if err := common.HandleAttributeFlags( c.Command, "attr", + c.FlagAttributes, + c.FlagAttrs, func() { - hostcatalogs.DefaultAttributes() + opts = append(opts, hostcatalogs.DefaultAttributes()) }, func(in map[string]interface{}) { - hostcatalogs.WithAttributes(in) + opts = append(opts, hostcatalogs.WithAttributes(in)) }); err != nil { c.PrintCliError(fmt.Errorf("Error evaluating attribute flags to: %s", err.Error())) return base.CommandCliError } + if err := common.HandleAttributeFlags( + c.Command, + "secret", + c.FlagSecrets, + c.FlagScrts, + func() { + opts = append(opts, hostcatalogs.DefaultSecrets()) + }, + func(in map[string]interface{}) { + opts = append(opts, hostcatalogs.WithSecrets(in)) + }); err != nil { + c.PrintCliError(fmt.Errorf("Error evaluating secret flags to: %s", err.Error())) + return base.CommandCliError + } + if ok := extraPluginFlagsHandlingFunc(c, f, &opts); !ok { return base.CommandUserError } diff --git a/internal/cmd/commands/hostsetscmd/funcs.go b/internal/cmd/commands/hostsetscmd/funcs.go index 933396332..532501bf7 100644 --- a/internal/cmd/commands/hostsetscmd/funcs.go +++ b/internal/cmd/commands/hostsetscmd/funcs.go @@ -273,6 +273,22 @@ func printItemTable(result api.GenericResult) string { ) } + if item.Plugin != nil { + ret = append(ret, + "", + " Plugin:", + base.PluginInfoForOutput(item.Plugin, maxLength), + ) + } + + if len(item.Attributes) > 0 { + ret = append(ret, + "", + " Attributes:", + base.WrapMap(4, maxLength, item.Attributes), + ) + } + if len(item.AuthorizedActions) > 0 { ret = append(ret, "", @@ -289,14 +305,6 @@ func printItemTable(result api.GenericResult) string { ) } - if len(item.Attributes) > 0 { - ret = append(ret, - "", - " Attributes:", - base.WrapMap(4, maxLength, item.Attributes), - ) - } - return base.WrapForHelpText(ret) } diff --git a/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go b/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go index f47e6cc76..8190cae04 100644 --- a/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go +++ b/internal/cmd/commands/hostsetscmd/plugin_hostsets.gen.go @@ -79,9 +79,9 @@ func (c *PluginCommand) Help() string { var flagsPluginMap = map[string][]string{ - "create": {"host-catalog-id", "name", "description"}, + "create": {"host-catalog-id", "name", "description", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, - "update": {"id", "name", "description", "version"}, + "update": {"id", "name", "description", "version", "attributes", "attr", "string-attr", "bool-attr", "num-attr"}, } func (c *PluginCommand) Flags() *base.FlagSets { @@ -93,6 +93,9 @@ func (c *PluginCommand) Flags() *base.FlagSets { f := set.NewFlagSet("Command Options") common.PopulateCommonFlags(c.Command, f, "plugin-type host set", flagsPluginMap, c.Func) + f = set.NewFlagSet("Attribute Options") + common.PopulateAttributeFlags(c.Command, f, flagsPluginMap, c.Func) + extraPluginFlagsFunc(c, set, f) return set @@ -175,6 +178,21 @@ func (c *PluginCommand) Run(args []string) int { } } + if err := common.HandleAttributeFlags( + c.Command, + "attr", + c.FlagAttributes, + c.FlagAttrs, + func() { + opts = append(opts, hostsets.DefaultAttributes()) + }, + func(in map[string]interface{}) { + opts = append(opts, hostsets.WithAttributes(in)) + }); err != nil { + c.PrintCliError(fmt.Errorf("Error evaluating attribute flags to: %s", err.Error())) + return base.CommandCliError + } + if ok := extraPluginFlagsHandlingFunc(c, f, &opts); !ok { return base.CommandUserError } diff --git a/internal/cmd/common/flags.go b/internal/cmd/common/flags.go index df15751d9..f7b197896 100644 --- a/internal/cmd/common/flags.go +++ b/internal/cmd/common/flags.go @@ -34,6 +34,20 @@ func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string, Completion: complete.PredictAnything, Usage: `Scope in which to make the request, identified by name.`, }) + case "plugin-id": + f.StringVar(&base.StringVar{ + Name: "plugin-id", + Target: &c.FlagPluginId, + Completion: complete.PredictAnything, + Usage: `ID of a plugin being referenced in the request.`, + }) + case "plugin-name": + f.StringVar(&base.StringVar{ + Name: "plugin-name", + Target: &c.FlagPluginName, + Completion: complete.PredictAnything, + Usage: `Name of a plugin being referenced in the request.`, + }) case "id": f.StringVar(&base.StringVar{ Name: "id", @@ -109,7 +123,7 @@ func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[stri f.StringVar(&base.StringVar{ Name: "attributes", Target: &c.FlagAttributes, - Usage: `A JSON map value to use as the entirety of the request's attribute map. Usually this will be soured from a file via "file://" syntax.`, + Usage: `A JSON map value to use as the entirety of the request's attributes map. Usually this will be sourced from a file via "file://" syntax.`, }) case "attr": f.CombinationSliceVar(&base.CombinationSliceVar{ @@ -118,7 +132,7 @@ func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[stri KvSplit: true, KeyDelimiter: &keyDelimiter, ProtoCompatKey: true, - Usage: `A key=value attribute to add to the request's attribute map. The type is automatically inferred. Use -string-attr, -bool-attr, or -num-attr if the type needs to be overridden. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + Usage: `A key=value attribute to add to the request's attributes map. The type is automatically inferred. Use -string-attr, -bool-attr, or -num-attr if the type needs to be overridden. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, }) case "string-attr": f.CombinationSliceVar(&base.CombinationSliceVar{ @@ -127,7 +141,7 @@ func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[stri KvSplit: true, KeyDelimiter: &keyDelimiter, ProtoCompatKey: true, - Usage: `A key=value string attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + Usage: `A key=value string attribute to add to the request's attributes map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, }) case "bool-attr": f.CombinationSliceVar(&base.CombinationSliceVar{ @@ -136,7 +150,7 @@ func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[stri KvSplit: true, KeyDelimiter: &keyDelimiter, ProtoCompatKey: true, - Usage: `A key=value bool attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + Usage: `A key=value bool attribute to add to the request's attributes map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, }) case "num-attr": f.CombinationSliceVar(&base.CombinationSliceVar{ @@ -145,7 +159,57 @@ func PopulateAttributeFlags(c *base.Command, f *base.FlagSet, flagNames map[stri KvSplit: true, KeyDelimiter: &keyDelimiter, ProtoCompatKey: true, - Usage: `A key=value numeric attribute to add to the request's attribute map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + Usage: `A key=value numeric attribute to add to the request's attributes map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + } + } +} + +func PopulateSecretFlags(c *base.Command, f *base.FlagSet, flagNames map[string][]string, command string) { + keyDelimiter := "." + for _, name := range flagNames[command] { + switch name { + case "secrets": + f.StringVar(&base.StringVar{ + Name: "secrets", + Target: &c.FlagSecrets, + Usage: `A JSON map value to use as the entirety of the request's secrets map. Usually this will be sourced from a file via "file://" syntax.`, + }) + case "secret": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "secret", + Target: &c.FlagScrts, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value secret to add to the request's secrets map. The type is automatically inferred. Use -string-secret, -bool-secret, or -num-secret if the type needs to be overridden. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "string-secret": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "string-secret", + Target: &c.FlagScrts, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value string secret to add to the request's secrets map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "bool-secret": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "bool-secret", + Target: &c.FlagScrts, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value bool secret to add to the request's secrets map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, + }) + case "num-secret": + f.CombinationSliceVar(&base.CombinationSliceVar{ + Name: "num-secret", + Target: &c.FlagScrts, + KvSplit: true, + KeyDelimiter: &keyDelimiter, + ProtoCompatKey: true, + Usage: `A key=value numeric secret to add to the request's secrets map. Can be specified multiple times. Supports sourcing values from files via "file://" and env vars via "env://"`, }) } } @@ -157,23 +221,23 @@ var jsonNumberRegex = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?$`) // HandleAttributeFlags takes in a command and a func to call for default (that // is, set to nil) and non-default values. Suffix can be used to allow this // logic to be used for various needs, e.g. -attr vs -secret. -func HandleAttributeFlags(c *base.Command, suffix string, defaultFunc func(), setFunc func(map[string]interface{})) error { - // If we were given -attributes, use that as-is - switch c.FlagAttributes { +func HandleAttributeFlags(c *base.Command, suffix, fullField string, sepFields []base.CombinedSliceFlagValue, defaultFunc func(), setFunc func(map[string]interface{})) error { + // If we were given a fullly defined field, use that as-is + switch fullField { case "": // Nothing, continue on case "null": defaultFunc() return nil default: - parsedString, err := parseutil.ParsePath(c.FlagAttributes) + parsedString, err := parseutil.ParsePath(fullField) if err != nil && !errors.Is(err, parseutil.ErrNotAUrl) { - return fmt.Errorf("error parsing attributes flag as a URL: %w", err) + return fmt.Errorf("error parsing %s flag as a URL: %w", suffix, err) } // We should be able to parse the string as a JSON object var setMap map[string]interface{} if err := json.Unmarshal([]byte(parsedString), &setMap); err != nil { - return fmt.Errorf("error parsing attributes flag as JSON: %w", err) + return fmt.Errorf("error parsing %s flag as JSON: %w", suffix, err) } setFunc(setMap) return nil @@ -181,8 +245,8 @@ func HandleAttributeFlags(c *base.Command, suffix string, defaultFunc func(), se setMap := map[string]interface{}{} - for _, attr := range c.FlagAttrs { - if len(attr.Keys) == 0 { + for _, field := range sepFields { + if len(field.Keys) == 0 { // No idea why this would happen, but skip it continue } @@ -191,101 +255,101 @@ func HandleAttributeFlags(c *base.Command, suffix string, defaultFunc func(), se var err error // First, perform any needed parsing if we are given the type - switch attr.Name { + switch field.Name { case "num-" + suffix: // JSON treats all numbers equally, however, we will try to be a // little better so that we don't include decimals if we don't need // to (and don't have to worry about precision if not necessary) - if strings.Contains(attr.Value, ".") { - val, err = strconv.ParseFloat(attr.Value, 64) + if strings.Contains(field.Value, ".") { + val, err = strconv.ParseFloat(field.Value, 64) if err != nil { - return fmt.Errorf("error parsing attribute %q as a float: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as a float: %w", field.Value, err) } } else { - val, err = strconv.ParseInt(attr.Value, 10, 64) + val, err = strconv.ParseInt(field.Value, 10, 64) if err != nil { - return fmt.Errorf("error parsing attribute %q as an integer: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as an integer: %w", field.Value, err) } } case "string-" + suffix: - val = strings.Trim(attr.Value, `"`) + val = strings.Trim(field.Value, `"`) case "bool-" + suffix: - switch attr.Value { + switch field.Value { case "true": val = true case "false": val = false default: - return fmt.Errorf("error parsing attribute %q as a bool", attr.Value) + return fmt.Errorf("error parsing value %q as a bool", field.Value) } case suffix: // In this case, use heuristics to just do the right thing the vast // majority of the time switch { - case attr.Value == "null": // Explicit null, we want to set to a null value to clear it + case field.Value == "null": // Explicit null, we want to set to a null value to clear it val = nil - case attr.Value == "true": // bool true + case field.Value == "true": // bool true val = true - case attr.Value == "false": // bool false + case field.Value == "false": // bool false val = false - case strings.HasPrefix(attr.Value, `"`): // explicitly quoted string - val = strings.Trim(attr.Value, `"`) + case strings.HasPrefix(field.Value, `"`): // explicitly quoted string + val = strings.Trim(field.Value, `"`) - case jsonNumberRegex.Match([]byte(strings.Trim(attr.Value, `"`))): // number + case jsonNumberRegex.Match([]byte(strings.Trim(field.Value, `"`))): // number // Same logic as above - if strings.Contains(attr.Value, ".") { - val, err = strconv.ParseFloat(attr.Value, 64) + if strings.Contains(field.Value, ".") { + val, err = strconv.ParseFloat(field.Value, 64) if err != nil { - return fmt.Errorf("error parsing attribute %q as a float: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as a float: %w", field.Value, err) } } else { - val, err = strconv.ParseInt(attr.Value, 10, 64) + val, err = strconv.ParseInt(field.Value, 10, 64) if err != nil { - return fmt.Errorf("error parsing attribute %q as an integer: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as an integer: %w", field.Value, err) } } - case strings.HasPrefix(attr.Value, "["): // serialized JSON array + case strings.HasPrefix(field.Value, "["): // serialized JSON array var s []interface{} - u := json.NewDecoder(bytes.NewBufferString(attr.Value)) + u := json.NewDecoder(bytes.NewBufferString(field.Value)) u.UseNumber() if err := u.Decode(&s); err != nil { - return fmt.Errorf("error parsing attribute %q as a json array: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as a json array: %w", field.Value, err) } val = s - case strings.HasPrefix(attr.Value, "{"): // serialized JSON map + case strings.HasPrefix(field.Value, "{"): // serialized JSON map var m map[string]interface{} - u := json.NewDecoder(bytes.NewBufferString(attr.Value)) + u := json.NewDecoder(bytes.NewBufferString(field.Value)) u.UseNumber() if err := u.Decode(&m); err != nil { - return fmt.Errorf("error parsing attribute %q as a json map: %w", attr.Value, err) + return fmt.Errorf("error parsing value %q as a json map: %w", field.Value, err) } val = m default: // Default is to treat as a string value - val = attr.Value + val = field.Value } default: - return fmt.Errorf("unknown attribute flag %q", attr.Name) + return fmt.Errorf("unknown flag %q", field.Name) } - // Now we have to insert it in the right position in the attribute map + // Now we have to insert it in the right position in the final map currMap := setMap - for i, segment := range attr.Keys { + for i, segment := range field.Keys { if segment == "" { - return fmt.Errorf("key segment %q for value %q is empty", segment, attr.Value) + return fmt.Errorf("key segment %q for value %q is empty", segment, field.Value) } switch { - case i == len(attr.Keys)-1: + case i == len(field.Keys)-1: // If we get an explicit "null" override whatever is currently // there if val == nil { diff --git a/internal/cmd/common/flags_test.go b/internal/cmd/common/flags_test.go index b4e3bd4d6..6d41bab66 100644 --- a/internal/cmd/common/flags_test.go +++ b/internal/cmd/common/flags_test.go @@ -2,6 +2,7 @@ package common import ( "encoding/json" + "fmt" "testing" "github.com/hashicorp/boundary/internal/cmd/base" @@ -170,12 +171,12 @@ func TestHandleAttributeFlags(t *testing.T) { name: "strings-only", args: []base.CombinedSliceFlagValue{ { - Name: "string-attr", + Name: "string-%s", Keys: []string{"foo"}, Value: "bar", }, { - Name: "string-attr", + Name: "string-%s", Keys: []string{"bar"}, Value: `"baz"`, }, @@ -189,12 +190,12 @@ func TestHandleAttributeFlags(t *testing.T) { name: "nums-only", args: []base.CombinedSliceFlagValue{ { - Name: "num-attr", + Name: "num-%s", Keys: []string{"foo"}, Value: "-1.2", }, { - Name: "num-attr", + Name: "num-%s", Keys: []string{"bar"}, Value: "5", }, @@ -208,7 +209,7 @@ func TestHandleAttributeFlags(t *testing.T) { name: "bad-float-num", args: []base.CombinedSliceFlagValue{ { - Name: "num-attr", + Name: "num-%s", Keys: []string{"foo"}, Value: "-15d.2", }, @@ -219,7 +220,7 @@ func TestHandleAttributeFlags(t *testing.T) { name: "bad-int-num", args: []base.CombinedSliceFlagValue{ { - Name: "num-attr", + Name: "num-%s", Keys: []string{"foo"}, Value: "-15d3", }, @@ -230,12 +231,12 @@ func TestHandleAttributeFlags(t *testing.T) { name: "bools-only", args: []base.CombinedSliceFlagValue{ { - Name: "bool-attr", + Name: "bool-%s", Keys: []string{"foo"}, Value: "true", }, { - Name: "bool-attr", + Name: "bool-%s", Keys: []string{"bar"}, Value: "false", }, @@ -249,7 +250,7 @@ func TestHandleAttributeFlags(t *testing.T) { name: "bad-bool", args: []base.CombinedSliceFlagValue{ { - Name: "bool-attr", + Name: "bool-%s", Keys: []string{"foo"}, Value: "t", }, @@ -260,47 +261,47 @@ func TestHandleAttributeFlags(t *testing.T) { name: "attr-only", args: []base.CombinedSliceFlagValue{ { - Name: "attr", + Name: "%s", Keys: []string{"b1"}, Value: "true", }, { - Name: "attr", + Name: "%s", Keys: []string{"b2"}, Value: "false", }, { - Name: "attr", + Name: "%s", Keys: []string{"s1"}, Value: "scoopde", }, { - Name: "attr", + Name: "%s", Keys: []string{"s2"}, Value: `"woop"`, }, { - Name: "attr", + Name: "%s", Keys: []string{"n1"}, Value: "-1.2", }, { - Name: "attr", + Name: "%s", Keys: []string{"n2"}, Value: "5", }, { - Name: "attr", + Name: "%s", Keys: []string{"a"}, Value: `["foo", 1.5, true, ["bar"], {"hip": "hop"}]`, }, { - Name: "attr", + Name: "%s", Keys: []string{"nil"}, Value: "null", }, { - Name: "attr", + Name: "%s", Keys: []string{"m"}, Value: `{"b": true, "n": 6, "s": "scoopde", "a": ["bar"], "m": {"hip": "hop"}}`, }, @@ -333,37 +334,37 @@ func TestHandleAttributeFlags(t *testing.T) { name: "map-array-structure", args: []base.CombinedSliceFlagValue{ { - Name: "attr", + Name: "%s", Keys: []string{"bools"}, Value: "true", }, { - Name: "attr", + Name: "%s", Keys: []string{"bools"}, Value: "false", }, { - Name: "attr", + Name: "%s", Keys: []string{"strings", "s1"}, Value: "scoopde", }, { - Name: "attr", + Name: "%s", Keys: []string{"strings", "s2"}, Value: `"woop"`, }, { - Name: "attr", + Name: "%s", Keys: []string{"numbers", "reps"}, Value: "-1.2", }, { - Name: "attr", + Name: "%s", Keys: []string{"numbers", "reps"}, Value: "5", }, { - Name: "attr", + Name: "%s", Keys: []string{"strings", "s2"}, // This will overwrite above! Value: "null", }, @@ -381,25 +382,32 @@ func TestHandleAttributeFlags(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) + for _, typ := range []string{"attr", "secret"} { + t.Run(fmt.Sprintf("%s-%s", tt.name, typ), func(t *testing.T) { + assert, require := assert.New(t), require.New(t) - // Note: we do the setup on each run to make sure we aren't carrying - // state over; just like in the real CLI where each run would have - // pristine state. - c := new(base.Command) - c.FlagAttrs = tt.args - var outMap map[string]interface{} + // Note: we do the setup on each run to make sure we aren't carrying + // state over; just like in the real CLI where each run would have + // pristine state. + c := new(base.Command) + var outMap map[string]interface{} - err := HandleAttributeFlags(c, "attr", func() {}, func(in map[string]interface{}) { outMap = in }) - if tt.expectedErr != "" { - require.Error(err) - assert.Contains(err.Error(), tt.expectedErr) - return - } + args := make([]base.CombinedSliceFlagValue, 0, len(tt.args)) + for _, arg := range tt.args { + arg.Name = fmt.Sprintf(arg.Name, typ) + args = append(args, arg) + } - require.NoError(err) - assert.Equal(tt.expectedMap, outMap) - }) + err := HandleAttributeFlags(c, typ, "", args, func() {}, func(in map[string]interface{}) { outMap = in }) + if tt.expectedErr != "" { + require.Error(err) + assert.Contains(err.Error(), tt.expectedErr) + return + } + + require.NoError(err) + assert.Equal(tt.expectedMap, outMap) + }) + } } } diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index e88fdefd1..0c57d3827 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -72,6 +72,13 @@ type cmdInfo struct { // HasGenericAttributes controls whether to generate flags for -attributes, // -attr, -string-attr, etc. HasGenericAttributes bool + + // HasGenericSecrets controls whether to generate flags for -secrets, + // -secret, -string-secret, etc. + HasGenericSecrets bool + + // IsPluginType controls whether standard plugin flags are generated + IsPluginType bool } var inputStructs = map[string][]*cmdInfo{ @@ -269,9 +276,11 @@ var inputStructs = map[string][]*cmdInfo{ HasName: true, HasDescription: true, Container: "Scope", + IsPluginType: true, VersionedActions: []string{"update"}, NeedsSubtypeInCreate: true, HasGenericAttributes: true, + HasGenericSecrets: true, }, }, "hostsets": { @@ -302,18 +311,19 @@ var inputStructs = map[string][]*cmdInfo{ VersionedActions: []string{"update"}, }, { - ResourceType: resource.HostSet.String(), - Pkg: "hostsets", - StdActions: []string{"create", "update"}, - SubActionPrefix: "plugin", - HasExtraCommandVars: true, - SkipNormalHelp: true, - HasExtraHelpFunc: true, - HasId: true, - HasName: true, - Container: "HostCatalog", - HasDescription: true, - VersionedActions: []string{"update"}, + ResourceType: resource.HostSet.String(), + Pkg: "hostsets", + StdActions: []string{"create", "update"}, + SubActionPrefix: "plugin", + HasExtraCommandVars: true, + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + Container: "HostCatalog", + HasDescription: true, + HasGenericAttributes: true, + VersionedActions: []string{"update"}, }, }, "hosts": { diff --git a/internal/cmd/gencli/templates.go b/internal/cmd/gencli/templates.go index 22d591a02..5c8f08854 100644 --- a/internal/cmd/gencli/templates.go +++ b/internal/cmd/gencli/templates.go @@ -170,15 +170,16 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Help() string { var flags{{ camelCase .SubActionPrefix }}Map = map[string][]string{ {{ with $attrFlags := ", \"attributes\", \"attr\", \"string-attr\", \"bool-attr\", \"num-attr\"" }} + {{ with $secretFlags := ", \"secrets\", \"secret\", \"string-secret\", \"bool-secret\", \"num-secret\"" }} {{ range $i, $action := $input.StdActions }} {{ if eq $action "create" }} - "create": { "{{ kebabCase $input.Container }}-id", "name", "description" {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} }, + "create": { "{{ kebabCase $input.Container }}-id", "name", "description" {{ if $input.IsPluginType }} , "plugin-id", "plugin-name" {{ end }} {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} {{ if $input.HasGenericSecrets }} {{ $secretFlags }} {{ end }} }, {{ end }} {{ if eq $action "read" }} "read": {"id"}, {{ end }} {{ if eq $action "update" }} - "update": {"id", "name", "description" {{ if hasAction $input.VersionedActions "update" }}, "version" {{ end }} {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} }, + "update": {"id", "name", "description" {{ if hasAction $input.VersionedActions "update" }}, "version" {{ end }} {{ if $input.HasGenericAttributes }} {{ $attrFlags }} {{ end }} {{ if $input.HasGenericSecrets }} {{ $secretFlags }} {{ end }} }, {{ end }} {{ if eq $action "delete" }} "delete": {"id"}, @@ -188,6 +189,7 @@ var flags{{ camelCase .SubActionPrefix }}Map = map[string][]string{ {{ end }} {{ end }} {{ end }} + {{ end }} } func (c *{{ camelCase .SubActionPrefix }}Command) Flags() *base.FlagSets { @@ -204,6 +206,11 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Flags() *base.FlagSets { common.PopulateAttributeFlags(c.Command, f, flags{{ camelCase .SubActionPrefix }}Map, c.Func) {{ end }} + {{ if .HasGenericSecrets }} + f = set.NewFlagSet("Secrets Options") + common.PopulateSecretFlags(c.Command, f, flags{{ camelCase .SubActionPrefix }}Map, c.Func) + {{ end }} + extra{{ camelCase .SubActionPrefix }}FlagsFunc(c, set, f) return set @@ -314,6 +321,19 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { opts = append(opts, {{ .Pkg }}.WithScopeName(c.FlagScopeName)) } {{ end }} + + {{ if .IsPluginType }} + switch c.FlagPluginId { + case "": + default: + opts = append(opts, {{ .Pkg }}.WithPluginId(c.FlagPluginId)) + } + switch c.FlagPluginName { + case "": + default: + opts = append(opts, {{ .Pkg }}.WithPluginName(c.FlagPluginName)) + } + {{ end }} var version uint32 {{ if .VersionedActions }} @@ -334,17 +354,36 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { if err := common.HandleAttributeFlags( c.Command, "attr", + c.FlagAttributes, + c.FlagAttrs, func() { - {{ .Pkg }}.DefaultAttributes() + opts = append(opts, {{ .Pkg }}.DefaultAttributes()) }, func(in map[string]interface{}) { - {{ .Pkg }}.WithAttributes(in) + opts = append(opts, {{ .Pkg }}.WithAttributes(in)) }); err != nil { c.PrintCliError(fmt.Errorf("Error evaluating attribute flags to: %s", err.Error())) return base.CommandCliError } {{ end }} + {{ if .HasGenericSecrets }} + if err := common.HandleAttributeFlags( + c.Command, + "secret", + c.FlagSecrets, + c.FlagScrts, + func() { + opts = append(opts, {{ .Pkg }}.DefaultSecrets()) + }, + func(in map[string]interface{}) { + opts = append(opts, {{ .Pkg }}.WithSecrets(in)) + }); err != nil { + c.PrintCliError(fmt.Errorf("Error evaluating secret flags to: %s", err.Error())) + return base.CommandCliError + } + {{ end }} + if ok := extra{{ camelCase .SubActionPrefix }}FlagsHandlingFunc(c, f, &opts, ); !ok { return base.CommandUserError } diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index f3f8afa34..1f3125ceb 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -1247,7 +1247,7 @@ }, { "name": "plugin_name", - "description": "As an alternative to providing the plugin id in the provided HostCatalog,\nthis field can be used to lookup the plugin using it's name.", + "description": "As an alternative to providing the plugin id in the provided HostCatalog,\nthis field can be used to lookup the plugin using its name.", "in": "query", "required": false, "type": "string" diff --git a/internal/gen/controller/api/services/host_catalog_service.pb.go b/internal/gen/controller/api/services/host_catalog_service.pb.go index 7bb089e0d..26edfda4f 100644 --- a/internal/gen/controller/api/services/host_catalog_service.pb.go +++ b/internal/gen/controller/api/services/host_catalog_service.pb.go @@ -235,8 +235,8 @@ type CreateHostCatalogRequest struct { Item *hostcatalogs.HostCatalog `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` // As an alternative to providing the plugin id in the provided HostCatalog, - // this field can be used to lookup the plugin using it's name. - PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + // this field can be used to lookup the plugin using its name. + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,proto3" json:"plugin_name,omitempty"` } func (x *CreateHostCatalogRequest) Reset() { @@ -576,113 +576,113 @@ var file_controller_api_services_v1_host_catalog_service_proto_rawDesc = []byte{ 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, + 0x74, 0x65, 0x6d, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x78, 0x0a, - 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x49, 0x0a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, - 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, - 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x19, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xde, 0x07, - 0x0a, 0x12, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, + 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x78, + 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x49, 0x0a, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, + 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, + 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xde, + 0x07, 0x0a, 0x12, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, + 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, + 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, + 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, - 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x2e, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, - 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, + 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x92, 0x41, 0x1f, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x73, 0x2e, 0x12, 0xc2, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, + 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, - 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x92, - 0x41, 0x1f, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, - 0x6f, 0x66, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, - 0x2e, 0x12, 0xc2, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x11, 0x2f, + 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, + 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x24, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x12, 0xbb, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x11, 0x2f, 0x76, - 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3a, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x12, 0xbb, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, - 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x42, 0x4d, - 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, + 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, + 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x42, + 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index 437b5ecb4..df151fef0 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -19,7 +19,7 @@ type HostCatalog struct { *store.HostCatalog tableName string `gorm:"-"` - secrets *structpb.Struct `gorm:"-"` + Secrets *structpb.Struct `gorm:"-"` } // NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId @@ -42,7 +42,7 @@ func NewHostCatalog(ctx context.Context, scopeId, pluginId string, opt ...Option Description: opts.withDescription, Attributes: attrs, }, - secrets: opts.withSecrets, + Secrets: opts.withSecrets, } return hc, nil } @@ -57,11 +57,11 @@ func allocHostCatalog() *HostCatalog { // secret. The secret shallow copied. func (c *HostCatalog) clone() *HostCatalog { cp := proto.Clone(c.HostCatalog) - newSecret := proto.Clone(c.secrets) + newSecret := proto.Clone(c.Secrets) hc := &HostCatalog{ HostCatalog: cp.(*store.HostCatalog), - secrets: newSecret.(*structpb.Struct), + Secrets: newSecret.(*structpb.Struct), } // proto.Clone will convert slices with length and capacity of 0 to nil. // Fix this since gorm treats empty slices differently than nil. diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index 5755b3db3..f52d98572 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -19,8 +19,9 @@ type Repository struct { kms *kms.Kms // plugins is a map from plugin resource id to host plugin client. - // TODO: When we are using go-plugin change from using a plgpb.HostPluginServiceServer - // to the client. + // + // TODO: When we are using go-plugin change from using a + // plgpb.HostPluginServiceServer to the client. plugins map[string]plgpb.HostPluginServiceServer // defaultLimit provides a default for limiting the number of results // returned from the repo diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 416815206..f788fe7ee 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -274,8 +274,8 @@ func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, err } hc.Attributes = attrs } - if in.secrets != nil { - hc.Secrets = in.secrets + if in.Secrets != nil { + hc.Secrets = in.Secrets } return hc, nil } diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index b4cc8c764..87b6664f4 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -212,7 +212,7 @@ func TestRepository_CreateCatalog(t *testing.T) { PluginId: plg.GetPublicId(), Attributes: []byte{}, }, - secrets: func() *structpb.Struct { + Secrets: func() *structpb.Struct { st, err := structpb.NewStruct(map[string]interface{}{ "k1": "v1", "k2": 2, @@ -276,7 +276,7 @@ func TestRepository_CreateCatalog(t *testing.T) { cSecret := allocHostCatalogSecret() err = rw.LookupWhere(ctx, &cSecret, "catalog_id=?", got.GetPublicId()) if tt.wantSecret == nil { - assert.Nil(got.secrets) + assert.Nil(got.Secrets) require.Error(t, err) require.True(t, errors.IsNotFoundError(err)) return diff --git a/internal/plugin/host/options.go b/internal/plugin/host/options.go index 3832ef15d..4bfbe5d13 100644 --- a/internal/plugin/host/options.go +++ b/internal/plugin/host/options.go @@ -1,7 +1,7 @@ package host -// getOpts - iterate the inbound Options and return a struct -func getOpts(opt ...Option) options { +// GetOpts - iterate the inbound Options and return a struct +func GetOpts(opt ...Option) options { opts := getDefaultOptions() for _, o := range opt { o(&opts) @@ -16,6 +16,7 @@ type Option func(*options) type options struct { withName string withDescription string + withPublicId string withLimit int } @@ -40,6 +41,13 @@ func WithName(name string) Option { } } +// WithPublicId provides an optional specific public ID +func WithPublicId(with string) Option { + return func(o *options) { + o.withPublicId = with + } +} + // WithLimit provides an option to provide a limit. Intentionally allowing // negative integers. If WithLimit < 0, then unlimited results are // returned. If WithLimit == 0, then default limits are used for results. diff --git a/internal/plugin/host/options_test.go b/internal/plugin/host/options_test.go index fed3d1c9f..7abe5829d 100644 --- a/internal/plugin/host/options_test.go +++ b/internal/plugin/host/options_test.go @@ -9,19 +9,25 @@ import ( func Test_GetOpts(t *testing.T) { t.Parallel() t.Run("WithName", func(t *testing.T) { - opts := getOpts(WithName("test")) + opts := GetOpts(WithName("test")) testOpts := getDefaultOptions() testOpts.withName = "test" assert.Equal(t, opts, testOpts) }) t.Run("WithDescription", func(t *testing.T) { - opts := getOpts(WithDescription("test desc")) + opts := GetOpts(WithDescription("test desc")) testOpts := getDefaultOptions() testOpts.withDescription = "test desc" assert.Equal(t, opts, testOpts) }) + t.Run("WithPublicId", func(t *testing.T) { + opts := GetOpts(WithPublicId("pl_1234567890")) + testOpts := getDefaultOptions() + testOpts.withPublicId = "pl_1234567890" + assert.Equal(t, opts, testOpts) + }) t.Run("WithLimit", func(t *testing.T) { - opts := getOpts(WithLimit(5)) + opts := GetOpts(WithLimit(5)) testOpts := getDefaultOptions() testOpts.withLimit = 5 assert.Equal(t, opts, testOpts) diff --git a/internal/plugin/host/plugin.go b/internal/plugin/host/plugin.go index a7ef7c9f5..fc5ec1c4f 100644 --- a/internal/plugin/host/plugin.go +++ b/internal/plugin/host/plugin.go @@ -17,7 +17,7 @@ type Plugin struct { // NewPlugin creates a new in memory Plugin assigned to the global scope. // Name, Description are the only allowed option. All other options are ignored. func NewPlugin(opt ...Option) *Plugin { - opts := getOpts(opt...) + opts := GetOpts(opt...) p := &Plugin{ Plugin: &store.Plugin{ ScopeId: scope.Global.String(), diff --git a/internal/plugin/host/plugin_test.go b/internal/plugin/host/plugin_test.go index 0bd6a3c30..1d7af800d 100644 --- a/internal/plugin/host/plugin_test.go +++ b/internal/plugin/host/plugin_test.go @@ -15,8 +15,6 @@ import ( func TestPlugin_Create(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") - type args struct{} - tests := []struct { name string opts []Option diff --git a/internal/plugin/host/repository.go b/internal/plugin/host/repository.go index a8604342a..f07c0bd72 100644 --- a/internal/plugin/host/repository.go +++ b/internal/plugin/host/repository.go @@ -32,7 +32,7 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") } - opts := getOpts(opt...) + opts := GetOpts(opt...) if opts.withLimit == 0 { // zero signals the boundary defaults should be used. opts.withLimit = db.DefaultLimit diff --git a/internal/plugin/host/repository_plugin.go b/internal/plugin/host/repository_plugin.go index e3669fefa..414840465 100644 --- a/internal/plugin/host/repository_plugin.go +++ b/internal/plugin/host/repository_plugin.go @@ -20,7 +20,7 @@ import ( // unique within p.ScopeID. // // Both p.CreateTime and c.UpdateTime are ignored. -func (r *Repository) CreatePlugin(ctx context.Context, p *Plugin, _ ...Option) (*Plugin, error) { +func (r *Repository) CreatePlugin(ctx context.Context, p *Plugin, opt ...Option) (*Plugin, error) { const op = "host.(Repository).CreatePlugin" if p == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "nil Plugin") @@ -39,11 +39,16 @@ func (r *Repository) CreatePlugin(ctx context.Context, p *Plugin, _ ...Option) ( } p = p.clone() - id, err := newPluginId() - if err != nil { - return nil, errors.Wrap(ctx, err, op) + opts := GetOpts(opt...) + + p.PublicId = opts.withPublicId + if p.PublicId == "" { + var err error + p.PublicId, err = newPluginId() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } } - p.PublicId = id oplogWrapper, err := r.kms.GetWrapper(ctx, p.ScopeId, kms.KeyPurposeOplog) if err != nil { @@ -122,7 +127,7 @@ func (r *Repository) ListPlugins(ctx context.Context, scopeIds []string, opt ... if len(scopeIds) == 0 { return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } - opts := getOpts(opt...) + opts := GetOpts(opt...) limit := r.defaultLimit if opts.withLimit != 0 { // non-zero signals an override of the default limit for the repo. diff --git a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto index 325768684..c2c8f917b 100644 --- a/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto +++ b/internal/proto/local/controller/api/resources/hostcatalogs/v1/host_catalog.proto @@ -23,7 +23,7 @@ message HostCatalog { resources.scopes.v1.ScopeInfo scope = 30; // The ID of the plugin of which this catalog is created. - string plugin_id = 34 [json_name="plugin_id"]; + string plugin_id = 34 [json_name="plugin_id", (custom_options.v1.generate_sdk_option) = true]; // Output only. Plugin information for this resource. resources.plugins.v1.PluginInfo plugin = 35; diff --git a/internal/proto/local/controller/api/services/v1/host_catalog_service.proto b/internal/proto/local/controller/api/services/v1/host_catalog_service.proto index ad6fc7dab..db8057d9e 100644 --- a/internal/proto/local/controller/api/services/v1/host_catalog_service.proto +++ b/internal/proto/local/controller/api/services/v1/host_catalog_service.proto @@ -104,8 +104,8 @@ message ListHostCatalogsResponse { message CreateHostCatalogRequest { api.resources.hostcatalogs.v1.HostCatalog item = 1; // As an alternative to providing the plugin id in the provided HostCatalog, - // this field can be used to lookup the plugin using it's name. - string plugin_name = 2; + // this field can be used to lookup the plugin using its name. + string plugin_name = 2 [json_name="plugin_name"]; } message CreateHostCatalogResponse { diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index eca57cf3e..41d01118d 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -24,7 +24,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" - plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/patrickmn/go-cache" @@ -104,6 +104,10 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } } + if conf.HostPlugins == nil { + conf.HostPlugins = make(map[string]plugin.HostPluginServiceServer) + } + // Set up repo stuff dbase := db.New(c.conf.Database) kmsRepo, err := kms.NewRepository(dbase, dbase) @@ -137,7 +141,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { return static.NewRepository(dbase, dbase, c.kms) } c.PluginHostRepoFn = func() (*hostplugin.Repository, error) { - return hostplugin.NewRepository(dbase, dbase, c.kms, map[string]plgpb.HostPluginServiceServer{}) + return hostplugin.NewRepository(dbase, dbase, c.kms, c.conf.HostPlugins) } c.HostPluginRepoFn = func() (*host.Repository, error) { return host.NewRepository(dbase, dbase, c.kms) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index d5879439c..a8db57fbc 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -50,9 +50,14 @@ var ( action.List, } - collectionTypeMap = map[resource.Type]action.ActionSet{ - resource.HostSet: host_sets.CollectionActions, - resource.Host: hosts.CollectionActions, + collectionTypeMap = map[string]map[resource.Type]action.ActionSet{ + "static": { + resource.HostSet: host_sets.CollectionActions, + resource.Host: hosts.CollectionActions, + }, + "plugin": { + resource.HostSet: host_sets.CollectionActions, + }, } ) @@ -121,11 +126,11 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs return &pbs.ListHostCatalogsResponse{}, nil } - ul, pluginInfoMap, err := s.listFromRepo(ctx, scopeIds) + items, pluginInfoMap, err := s.listFromRepo(ctx, scopeIds) if err != nil { return nil, err } - if len(ul) == 0 { + if len(items) == 0 { return &pbs.ListHostCatalogsResponse{}, nil } @@ -133,11 +138,11 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs if err != nil { return nil, err } - finalItems := make([]*pb.HostCatalog, 0, len(ul)) + finalItems := make([]*pb.HostCatalog, 0, len(items)) res := perms.Resource{ Type: resource.HostCatalog, } - for _, item := range ul { + for _, item := range items { res.Id = item.GetPublicId() res.ScopeId = item.GetScopeId() authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings() @@ -155,11 +160,20 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, item.GetPublicId()) - if err != nil { - return nil, err + var collType string + switch item.(type) { + case *static.HostCatalog: + collType = "static" + case *plugin.HostCatalog: + collType = "plugin" + } + if collType != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, item.GetPublicId()) + if err != nil { + return nil, err + } + outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } - outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } switch hc := item.(type) { case *plugin.HostCatalog: @@ -210,11 +224,20 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId()) - if err != nil { - return nil, err + var collType string + switch hc.(type) { + case *static.HostCatalog: + collType = "static" + case *plugin.HostCatalog: + collType = "plugin" + } + if collType != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, hc.GetPublicId()) + if err != nil { + return nil, err + } + outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } - outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } if plg != nil { outputOpts = append(outputOpts, handlers.WithPlugin(plg)) @@ -258,11 +281,20 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId()) - if err != nil { - return nil, err + var collType string + switch hc.(type) { + case *static.HostCatalog: + collType = "static" + case *plugin.HostCatalog: + collType = "plugin" + } + if collType != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, hc.GetPublicId()) + if err != nil { + return nil, err + } + outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } - outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions)) } if plg != nil { outputOpts = append(outputOpts, handlers.WithPlugin(plg)) @@ -309,7 +341,7 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId()) + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap["static"], authResults.Scope.Id, hc.GetPublicId()) if err != nil { return nil, err } @@ -408,7 +440,7 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]host.Ca } func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) { - const op = "host_catalogs.(Service).createInRepo" + const op = "host_catalogs.(Service).createStaticInRepo" h, err := toStorageStaticCatalog(ctx, projId, item) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build catalog for creation")) @@ -428,7 +460,7 @@ func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb } func (s Service) createPluginInRepo(ctx context.Context, projId string, req *pbs.CreateHostCatalogRequest) (*plugin.HostCatalog, *plugins.PluginInfo, error) { - const op = "host_catalogs.(Service).createInRepo" + const op = "host_catalogs.(Service).createPluginInRepo" item := req.GetItem() pluginId := item.GetPluginId() if pluginId == "" { @@ -716,13 +748,15 @@ func validateCreateRequest(req *pbs.CreateHostCatalogRequest) error { case static.Subtype: case plugin.Subtype: if req.GetItem().GetPlugin() != nil { - badFields[globals.PluginField] = fmt.Sprintf("This is a read only field.") + badFields[globals.PluginField] = "This is a read only field." } if req.GetItem().GetPluginId() == "" && req.GetPluginName() == "" { - badFields[globals.PluginIdField] = fmt.Sprintf("This is a required field.") + badFields[globals.PluginIdField] = "This or plugin name is a required field." + badFields[globals.PluginNameField] = "This or plugin id is a required field." } if req.GetItem().GetPluginId() != "" && req.GetPluginName() != "" { - badFields[globals.PluginIdField] = fmt.Sprintf("Can't set the plugin name field along with this field.") + badFields[globals.PluginIdField] = "Can't set the plugin name field along with this field." + badFields[globals.PluginNameField] = "Can't set the plugin id field along with this field." } default: badFields[globals.TypeField] = fmt.Sprintf("This is a required field and must be either %q or %q.", static.Subtype.String(), plugin.Subtype.String()) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 797ea630d..a0f496094 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -36,17 +36,27 @@ import ( "github.com/stretchr/testify/require" ) -var authorizedCollectionActions = map[string]*structpb.ListValue{ - "host-sets": { - Values: []*structpb.Value{ - structpb.NewStringValue("create"), - structpb.NewStringValue("list"), +var authorizedCollectionActions = map[string]map[string]*structpb.ListValue{ + "static": { + "host-sets": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, + }, + "hosts": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, }, }, - "hosts": { - Values: []*structpb.Value{ - structpb.NewStringValue("create"), - structpb.NewStringValue("list"), + "plugin": { + "host-sets": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, }, }, } @@ -86,7 +96,7 @@ func TestGet_Static(t *testing.T) { UpdatedTime: hc.UpdateTime.GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], } cases := []struct { @@ -187,7 +197,7 @@ func TestGet_Plugin(t *testing.T) { UpdatedTime: hc.UpdateTime.GetTimestamp(), Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["plugin"], } cases := []struct { @@ -277,7 +287,7 @@ func TestList(t *testing.T) { Version: 1, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }) } @@ -301,7 +311,7 @@ func TestList(t *testing.T) { Version: 1, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["plugin"], } wantSomeCatalogs = append(wantSomeCatalogs, cat) testPluginCatalogs = append(testPluginCatalogs, cat) @@ -318,7 +328,7 @@ func TestList(t *testing.T) { Version: 1, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }) } @@ -341,7 +351,7 @@ func TestList(t *testing.T) { Version: 1, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["plugin"], }) } @@ -597,7 +607,7 @@ func TestCreate_Static(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -764,7 +774,7 @@ func TestCreate_Plugin(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["plugin"], }, }, }, @@ -951,7 +961,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -977,7 +987,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -1032,7 +1042,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -1055,7 +1065,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -1080,7 +1090,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, @@ -1105,7 +1115,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions, + AuthorizedCollectionActions: authorizedCollectionActions["static"], }, }, }, diff --git a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go index 988b3ccb9..2b318fed2 100644 --- a/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go +++ b/sdk/pbs/controller/api/resources/hostcatalogs/host_catalog.pb.go @@ -226,7 +226,7 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_rawDesc = [ 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf4, 0x07, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfa, 0x07, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, @@ -234,68 +234,69 @@ var file_controller_api_resources_hostcatalogs_v1_host_catalog_proto_rawDesc = [ 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x5f, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x23, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, - 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, - 0x18, 0x6e, 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, 0x42, - 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2f, - 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x9d, 0x01, 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, - 0x6a, 0x0a, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x09, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, + 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, + 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, + 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, - 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, - 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, + 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6a, 0x0a, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, + 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 8b1ca4b0b8b9d8d011c484fecdcba75a789f4765 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 28 Sep 2021 16:19:32 -0400 Subject: [PATCH 26/71] Add host IDs to plugin host sets read response (#1568) This also includes tweaks here and there within the service and repos. --- internal/host/options.go | 11 ++ internal/host/plugin/loopback.go | 44 ++++-- internal/host/plugin/loopback_test.go | 139 ++++++++++++++++++ internal/host/plugin/repository_host_set.go | 89 ++++++++--- .../host/plugin/repository_host_set_test.go | 2 +- .../handlers/host_sets/host_set_service.go | 13 +- .../host_sets/host_set_service_test.go | 14 +- 7 files changed, 269 insertions(+), 43 deletions(-) diff --git a/internal/host/options.go b/internal/host/options.go index 6e2afe4d6..87d27c155 100644 --- a/internal/host/options.go +++ b/internal/host/options.go @@ -19,6 +19,7 @@ type options struct { WithLimit int WithOrderByCreateTime bool Ascending bool + WithSetMembers bool } func getDefaultOptions() options { @@ -44,3 +45,13 @@ func WithOrderByCreateTime(ascending bool) Option { return nil } } + +// WithSetMembers controls whether to include set members in a lookup. This will +// always be true for static and may be true for plugin when we have caching, +// but for now this skips API calls on authentication. +func WithSetMembers(with bool) Option { + return func(o *options) error { + o.WithSetMembers = true + return nil + } +} diff --git a/internal/host/plugin/loopback.go b/internal/host/plugin/loopback.go index 7202493a4..cfd3c06fd 100644 --- a/internal/host/plugin/loopback.go +++ b/internal/host/plugin/loopback.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "fmt" "github.com/hashicorp/boundary/internal/errors" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" @@ -20,7 +21,7 @@ var _ plgpb.HostPluginServiceServer = (*loopbackPlugin)(nil) type loopbackPlugin struct { *TestPluginServer - hostMap map[string]*loopbackPluginHostInfo + hostMap map[string][]*loopbackPluginHostInfo } type loopbackPluginHostInfo struct { @@ -33,7 +34,7 @@ type loopbackPluginHostInfo struct { func NewLoopbackPlugin() plgpb.HostPluginServiceServer { ret := &loopbackPlugin{ TestPluginServer: new(TestPluginServer), - hostMap: make(map[string]*loopbackPluginHostInfo), + hostMap: make(map[string][]*loopbackPluginHostInfo), } ret.OnCreateCatalogFn = ret.onCreateCatalog ret.OnCreateSetFn = ret.onCreateSet @@ -71,11 +72,24 @@ func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSet if attrs := set.GetAttributes(); attrs != nil { attrsMap := attrs.AsMap() if field := attrsMap[loopbackPluginHostInfoAttrField]; field != nil { - hostInfo := new(loopbackPluginHostInfo) - if err := mapstructure.Decode(field, hostInfo); err != nil { - return nil, errors.Wrap(ctx, err, op) + switch t := field.(type) { + case []interface{}: + for _, h := range t { + hostInfo := new(loopbackPluginHostInfo) + if err := mapstructure.Decode(h, hostInfo); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + l.hostMap[set.GetId()] = append(l.hostMap[set.GetId()], hostInfo) + } + case map[string]interface{}: + hostInfo := new(loopbackPluginHostInfo) + if err := mapstructure.Decode(t, hostInfo); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + l.hostMap[set.GetId()] = append(l.hostMap[set.GetId()], hostInfo) + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown host info type %T", t)) } - l.hostMap[set.GetId()] = hostInfo } } return nil, nil @@ -101,16 +115,18 @@ func (l *loopbackPlugin) listHosts(ctx context.Context, req *plgpb.ListHostsRequ } resp := new(plgpb.ListHostsResponse) for _, set := range req.GetSets() { - hostInfo := l.hostMap[set.GetId()] - if hostInfo == nil { + hostInfos := l.hostMap[set.GetId()] + if len(hostInfos) == 0 { continue } - resp.Hosts = append(resp.Hosts, &plgpb.ListHostsResponseHost{ - SetIds: []string{set.GetId()}, - ExternalId: hostInfo.ExternalId, - IpAddresses: hostInfo.IpAddresses, - DnsNames: hostInfo.DnsNames, - }) + for _, host := range hostInfos { + resp.Hosts = append(resp.Hosts, &plgpb.ListHostsResponseHost{ + SetIds: []string{set.GetId()}, + ExternalId: host.ExternalId, + IpAddresses: host.IpAddresses, + DnsNames: host.DnsNames, + }) + } } return resp, nil } diff --git a/internal/host/plugin/loopback_test.go b/internal/host/plugin/loopback_test.go index ec73cd731..6bd0dfcf1 100644 --- a/internal/host/plugin/loopback_test.go +++ b/internal/host/plugin/loopback_test.go @@ -197,3 +197,142 @@ func TestLoopbackPlugin(t *testing.T) { }) } } + +func TestLoopbackPluginArrays(t *testing.T) { + require := tr.New(t) + ctx := context.Background() + + plg := NewLoopbackPlugin() + + // Add data to some sets + hostInfo1 := map[string]interface{}{ + loopbackPluginHostInfoAttrField: []interface{}{ + map[string]interface{}{ + "set_ids": []interface{}{"set1"}, + "external_id": "host1a", + "ip_addresses": []interface{}{"1.2.3.4", "2.3.4.5"}, + "dns_names": []interface{}{"foo.com"}, + }, + map[string]interface{}{ + "set_ids": []interface{}{"set1"}, + "external_id": "host1b", + "ip_addresses": []interface{}{"3.4.5.6", "4.5.6.7"}, + "dns_names": []interface{}{"bar.com"}, + }, + }, + } + attrs, err := structpb.NewStruct(hostInfo1) + require.NoError(err) + _, err = plg.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{ + Set: &hostsets.HostSet{ + Id: "set1", + Attributes: attrs, + }, + }) + require.NoError(err) + hostInfo2 := map[string]interface{}{ + loopbackPluginHostInfoAttrField: []interface{}{ + map[string]interface{}{ + "set_ids": []interface{}{"set2"}, + "external_id": "host2a", + "ip_addresses": []interface{}{"10.20.30.40", "20.30.40.50"}, + "dns_names": []interface{}{"foz.com"}, + }, + map[string]interface{}{ + "set_ids": []interface{}{"set2"}, + "external_id": "host2b", + "ip_addresses": []interface{}{"30.40.50.60", "40.50.60.70"}, + "dns_names": []interface{}{"baz.com"}, + }, + }, + } + attrs, err = structpb.NewStruct(hostInfo2) + require.NoError(err) + _, err = plg.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{ + Set: &hostsets.HostSet{ + Id: "set2", + Attributes: attrs, + }, + }) + require.NoError(err) + + // Define test struct and validation function + type testInfo struct { + name string + sets []string + found []interface{} + } + validateSets := func(t *testing.T, tt testInfo) { + require, assert := tr.New(t), ta.New(t) + var hostSets []*hostsets.HostSet + for _, set := range tt.sets { + hostSets = append(hostSets, &hostsets.HostSet{Id: set}) + } + resp, err := plg.ListHosts(ctx, &plgpb.ListHostsRequest{ + Sets: hostSets, + }) + require.NoError(err) + if len(tt.found) == 0 { + assert.Len(resp.GetHosts(), 0) + return + } + + require.Greater(len(resp.GetHosts()), 0) + + var found []interface{} + for _, host := range resp.GetHosts() { + hostMap := map[string]interface{}{ + "external_id": host.GetExternalId(), + } + var sets []interface{} + for _, set := range host.SetIds { + sets = append(sets, set) + } + var ips []interface{} + for _, ip := range host.GetIpAddresses() { + ips = append(ips, ip) + } + var names []interface{} + for _, name := range host.GetDnsNames() { + names = append(names, name) + } + if len(sets) > 0 { + hostMap["set_ids"] = sets + } + if len(ips) > 0 { + hostMap["ip_addresses"] = ips + } + if len(names) > 0 { + hostMap["dns_names"] = names + } + found = append(found, hostMap) + } + assert.ElementsMatch(tt.found, found) + } + + // First set of tests: check that we can look up sets individually and + // together + setTests := []testInfo{ + { + name: "set 1", + sets: []string{"set1"}, + found: hostInfo1[loopbackPluginHostInfoAttrField].([]interface{}), + }, + { + name: "set 2", + sets: []string{"set2"}, + found: hostInfo2[loopbackPluginHostInfoAttrField].([]interface{}), + }, + { + name: "sets 1 and 2", + sets: []string{"set1", "set2"}, + found: append(hostInfo1[loopbackPluginHostInfoAttrField].([]interface{}), + hostInfo2[loopbackPluginHostInfoAttrField].([]interface{})...), + }, + } + for _, tt := range setTests { + t.Run(tt.name, func(t *testing.T) { + validateSets(t, tt) + }) + } +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 3abda7fc7..61e2337f7 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + hspb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -153,28 +154,69 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return returnedHostSet, plg, nil } -// LookupSet will look up a host set in the repository and return the host -// set. If the host set is not found, it will return nil, nil. -// All options are ignored. -func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, *hostplugin.Plugin, error) { +// LookupSet will look up a host set in the repository and return the host set, +// as well as host IDs that match. If the host set is not found, it will return +// nil, nil, nil, nil. Supported options: WithSetMembers, which requests that +// host IDs contained within the set are looked up and returned. (In the future +// we may make it automatic to return this if it's coming from the database.) +func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, []string, *hostplugin.Plugin, error) { const op = "plugin.(Repository).LookupSet" if publicId == "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + + opts, err := host.GetOpts(opt...) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) } sets, plg, err := r.getSets(ctx, publicId, "", opt...) if err != nil { - return nil, nil, errors.Wrap(ctx, err, op) + return nil, nil, nil, errors.Wrap(ctx, err, op) } switch { case len(sets) == 0: - return nil, nil, nil // not an error to return no rows for a "lookup" + return nil, nil, nil, nil // not an error to return no rows for a "lookup" case len(sets) > 1: - return nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) - default: - return sets[0], plg, nil + return nil, nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) } + + setToReturn := sets[0] + var hostIdsToReturn []string + + if plg != nil && opts.WithSetMembers { + plgSet, err := toPluginSet(ctx, setToReturn) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + plgClient, ok := r.plugins[plg.GetPublicId()] + if !ok { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("no plugin found for plugin id %s", plg.GetPublicId())) + } + resp, err := plgClient.ListHosts(ctx, &plgpb.ListHostsRequest{ + Sets: []*hspb.HostSet{plgSet}, + }) + switch { + case err != nil: + // If it's just not implemented, e.g. for tests, don't error out, return what we have + if status.Code(err) != codes.Unimplemented { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + case resp != nil: + for _, respHost := range resp.GetHosts() { + hostId, err := newHostId(ctx, setToReturn.GetCatalogId(), respHost.GetExternalId()) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + hostIdsToReturn = append(hostIdsToReturn, hostId) + } + } + } + + sort.Strings(hostIdsToReturn) + + return setToReturn, hostIdsToReturn, plg, nil } // ListSets returns a slice of HostSets for the catalogId. WithLimit is the @@ -337,7 +379,7 @@ func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_va // toPluginSet returns a host set in the format expected by the host plugin system. func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { - const op = "plugin.toPluginCatalog" + const op = "plugin.toPluginSet" if in == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin") } @@ -363,6 +405,8 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En if len(setIds) == 0 { return nil, errors.New(ctx, errors.InvalidParameter, op, "no set ids") } + + // Fist, look up the sets corresponding to the set IDs var setAggs []*hostSetAgg if err := r.reader.SearchWhere(ctx, &setAggs, "public_id in (?)", []interface{}{setIds}); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve sets %v", setIds))) @@ -377,13 +421,15 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En } type catalogInfo struct { - publicId string - plg plgpb.HostPluginServiceServer - setInfos map[string]*setInfo - plgCat *hcpb.HostCatalog - persisted *plgpb.HostCatalogPersisted + publicId string // ID of the catalog + plg plgpb.HostPluginServiceServer // plugin client for the catalog + setInfos map[string]*setInfo // map of set IDs to set information + plgCat *hcpb.HostCatalog // storage host catalog + persisted *plgpb.HostCatalogPersisted // host catalog persisted (secret) data } + // Next, look up the distinct catalog info and assign set infos to it. + // Notably, this does not include persisted info. catalogInfos := make(map[string]*catalogInfo) for _, ag := range setAggs { ci, ok := catalogInfos[ag.CatalogId] @@ -416,6 +462,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En catalogInfos[ag.CatalogId] = ci } + // Now, look up the catalog persisted (secret) information catIds := make([]string, 0, len(catalogInfos)) for k := range catalogInfos { catIds = append(catIds, k) @@ -438,7 +485,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En } ci.plgCat = plgCat - // TODO: Do these looksups from the DB in bulk instead of individually. + // TODO: Do these lookups from the DB in bulk instead of individually. per, err := r.getPersistedDataForCatalog(ctx, c) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("persisted catalog lookup failed")) @@ -451,6 +498,8 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En var hosts []interface{} hostIds := map[string]bool{} var es []*host.Endpoint + + // For each distinct catalog, list all sets at once for _, ci := range catalogInfos { var sets []*pb.HostSet @@ -459,9 +508,9 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En } resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ - Catalog: ci.plgCat, - Sets: sets, - // Persisted: ci.persisted, + Catalog: ci.plgCat, + Sets: sets, + Persisted: ci.persisted, }) if err != nil { return nil, errors.Wrap(ctx, err, op) diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 3327ab13b..ffd6dba73 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -352,7 +352,7 @@ func TestRepository_LookupSet(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) - got, _, err := repo.LookupSet(ctx, tt.in) + got, _, _, err := repo.LookupSet(ctx, tt.in) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 8cae153bb..899d6b5cb 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -9,6 +9,7 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin" + plugstore "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/libs/endpoint" @@ -422,7 +423,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.H if err != nil { return nil, nil, nil, err } - hset, hsplg, err := repo.LookupSet(ctx, id) + hset, hostIds, hsplg, err := repo.LookupSet(ctx, id, host.WithSetMembers(true)) if err != nil { return nil, nil, nil, err } @@ -431,6 +432,14 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.H } hs = hset plg = toPluginInfo(hsplg) + for _, h := range hostIds { + hl = append(hl, &plugin.Host{ + Host: &plugstore.Host{ + PublicId: h, + CatalogId: hset.CatalogId, + }, + }) + } } return hs, hl, plg, nil } @@ -668,7 +677,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty } set = ss case plugin.Subtype: - ps, _, err := pluginRepo.LookupSet(ctx, id) + ps, _, _, err := pluginRepo.LookupSet(ctx, id) if err != nil { res.Error = err return nil, res diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index a772e29cc..d8b6eebd9 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -148,17 +148,19 @@ func TestGet_Plugin(t *testing.T) { repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) - } name := "test" prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} plg := hostplugin.TestPlugin(t, conn, name) - hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, kms, hc, map[string]plgpb.HostPluginServiceServer{ + plgm := map[string]plgpb.HostPluginServiceServer{ plg.GetPublicId(): &plugin.TestPluginServer{}, - }, plugin.WithPreferredEndpoints(prefEndpoints)) + } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, plgm) + } + + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + hs := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints)) toMerge := &pbs.GetHostSetRequest{} From e11a4812568b607333b2f194d536c3ef41069bdd Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 29 Sep 2021 15:46:42 -0700 Subject: [PATCH 27/71] Register Azure and AWS at controller start up (#1570) * Register Plugins. * Pass catalogs and persisted data when listing hosts for host sets. Register azure plugin at startup if not present. --- go.mod | 6 +- go.sum | 82 +++++++++++++++---- internal/cmd/base/dev.go | 3 +- internal/cmd/base/initial_resources.go | 23 +++++- internal/host/plugin/repository_host_set.go | 14 ++++ .../host/plugin/repository_host_set_test.go | 8 +- internal/servers/controller/controller.go | 15 +++- .../host_catalogs/host_catalog_service.go | 3 + internal/servers/controller/testing.go | 2 + 9 files changed, 125 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 9bd96aad3..cb2a711b9 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,10 @@ require ( github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d github.com/hashicorp/boundary/api v0.0.18 - github.com/hashicorp/boundary/sdk v0.0.10 + github.com/hashicorp/boundary/sdk v0.0.11-0.20210928201932-8b1ca4b0b8b9 github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 @@ -54,7 +56,6 @@ require ( github.com/kr/text v0.2.0 github.com/lib/pq v1.10.3 github.com/mattn/go-colorable v0.1.8 - github.com/mattn/go-isatty v0.0.13 // indirect github.com/mitchellh/cli v1.1.2 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/go-wordwrap v1.0.1 @@ -81,7 +82,6 @@ require ( google.golang.org/grpc v1.40.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 google.golang.org/protobuf v1.27.1 - gopkg.in/yaml.v2 v2.4.0 // indirect mvdan.cc/gofumpt v0.1.1 nhooyr.io/websocket v1.8.7 ) diff --git a/go.sum b/go.sum index d145070bd..b74d48641 100644 --- a/go.sum +++ b/go.sum @@ -39,38 +39,55 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.2.9 h1:LWvJtUswz/W9/zVVXELrmlvdwWcKE60ZAw0FWV9vssk= github.com/AlecAivazis/survey/v2 v2.2.9/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= -github.com/Azure/azure-sdk-for-go v36.2.0+incompatible h1:09cv2WoH0g6jl6m2iT+R9qcIPZKhXEL0sbmLhxP895s= github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v57.4.0+incompatible h1:uzhy/mTwu4rq6Be/fJJnE82PRx2A2vjGjzsA3aQ6Qu0= +github.com/Azure/azure-sdk-for-go v57.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= -github.com/Azure/go-autorest/autorest v0.10.1 h1:uaB8A32IZU9YKs9v50+/LWIWTDHJk2vlGzbfd7FfESI= github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= +github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= +github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= -github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= -github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -111,6 +128,7 @@ github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= @@ -123,8 +141,9 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.40.44 h1:kECaYybTWYZY5IKHvQMxbE6Wi5Qrb+7hbkV7zQV3Sg8= +github.com/aws/aws-sdk-go v1.40.44/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -203,14 +222,14 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos= github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= github.com/dhui/dktest v0.3.4 h1:VbUEcaSP+U2/yUr9d2JhSThXYEnDlGabRSHe2rIE46E= github.com/dhui/dktest v0.3.4/go.mod h1:4m4n6lmXlmVfESth7mzdcv8nBI5mOb5UROPqjM02csU= -github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/docker/cli v20.10.7+incompatible h1:pv/3NqibQKphWZiAskMzdz8w0PRbtTaEB+f6NwdU7Is= github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= @@ -245,6 +264,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/favadi/protoc-go-inject-tag v1.3.0 h1:JPrmsmc/uBShG85uY5xGZIa5WJ0IaNZn6LZhQR9tIQE= github.com/favadi/protoc-go-inject-tag v1.3.0/go.mod h1:SSkUBgfqw2IJ2p7NPNKWk0Idwxt/qIt2LQgFPUgRGtc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -324,6 +345,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -401,6 +423,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 h1:XrH7otQgHtqcYX1qbO10DypzjEBwYdtYcIEc9d1BG5w= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578/go.mod h1:TbqGUjL8yDVqdoEHCiEPoE7lv5yfQRKxqPinfGUeC30= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d h1:WPcdLfDLQVhejIbvSfNp3uV5lJ42eLktqKfUpj1NP4w= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d/go.mod h1:dtbUh5NNMbV9pZNncSNo3JPoG3zm9LeMBYz+lKVdv0c= github.com/hashicorp/cap v0.1.1 h1:GjO4+9+H0wv/89YoEsxeVc2jIizL19r5v5l2lpaH8Kg= github.com/hashicorp/cap v0.1.1/go.mod h1:VfBvK2ULRyqsuqAnjgZl7HJ7/CGMC7ro4H5eXiZuun8= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -455,8 +481,9 @@ github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR3 github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2 h1:AEcLbDoaRC9JMmtZXsuCykztH53rvHsQFnwhoKtpNQM= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 h1:aCPwC2WPyomaw5gk5ZZ7DPevN8d9DS3igkmq6DxGMjU= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 h1:6KMBnfEv0/kLAz0O76sliN5mXbCDcLfs2kP7ssP7+DQ= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7mduW2Q+2FMrXawkLM8e7NU= @@ -489,8 +516,9 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -502,6 +530,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= +github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= @@ -572,8 +602,11 @@ github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= @@ -619,6 +652,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -631,6 +665,8 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/manicminer/hamilton v0.30.0 h1:n7og9/Xh4d/JX0XG0uG1rv2vlkIkz8jwynCWoQszZvA= +github.com/manicminer/hamilton v0.30.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -798,6 +834,7 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -869,6 +906,7 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 h1:MCfT24H3f//U5+UCrZp1 github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= @@ -890,6 +928,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE= github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8= +github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -942,6 +982,7 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA= @@ -983,6 +1024,7 @@ golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1031,6 +1073,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1040,6 +1084,7 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1225,8 +1270,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= diff --git a/internal/cmd/base/dev.go b/internal/cmd/base/dev.go index 5ab9a3ec1..15bae43d9 100644 --- a/internal/cmd/base/dev.go +++ b/internal/cmd/base/dev.go @@ -147,11 +147,10 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error { pluginId, plg := opts.withHostPlugin() b.DevLoopbackHostPluginId = pluginId opts := []hostplugin.Option{ - hostplugin.WithName("loopback"), hostplugin.WithDescription("Provides an initial loopback host plugin in Boundary"), hostplugin.WithPublicId(b.DevLoopbackHostPluginId), } - if _, err = b.CreateHostPlugin(ctx, plg, opts...); err != nil { + if _, err = b.CreateHostPlugin(ctx, "loopback", plg, opts...); err != nil { return err } } diff --git a/internal/cmd/base/initial_resources.go b/internal/cmd/base/initial_resources.go index bc7080b8c..799f1ae10 100644 --- a/internal/cmd/base/initial_resources.go +++ b/internal/cmd/base/initial_resources.go @@ -587,7 +587,14 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) return tt, nil } -func (b *Server) CreateHostPlugin(ctx context.Context, plg plgpb.HostPluginServiceServer, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { +// CreateHostPlugin creates a host plugin in the database if not present. +// It also registers the plugin in the shared map of running plugins. Since +// all boundary provided host plugins must have a name, a name is required +// when calling CreateHostPlugin and will be used even if WithName is provided. +func (b *Server) CreateHostPlugin(ctx context.Context, name string, plg plgpb.HostPluginServiceServer, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { + if name == "" { + return nil, fmt.Errorf("no name provided when creating plugin.") + } rw := db.New(b.Database) kmsRepo, err := kms.NewRepository(rw, rw) @@ -619,10 +626,18 @@ func (b *Server) CreateHostPlugin(ctx context.Context, plg plgpb.HostPluginServi return nil, fmt.Errorf("error creating host plugin repository: %w", err) } - plugin := hostplugin.NewPlugin(opt...) - plugin, err = hpRepo.CreatePlugin(cancelCtx, plugin, opt...) + plugin, err := hpRepo.LookupPluginByName(ctx, name) if err != nil { - return nil, fmt.Errorf("error creating host plugin: %w", err) + return nil, fmt.Errorf("error looking up host plugin by name: %w", err) + } + + if plugin == nil { + opt = append(opt, hostplugin.WithName(name)) + plugin = hostplugin.NewPlugin(opt...) + plugin, err = hpRepo.CreatePlugin(cancelCtx, plugin, opt...) + if err != nil { + return nil, fmt.Errorf("error creating host plugin: %w", err) + } } if b.HostPlugins == nil { diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 61e2337f7..bc13adc0e 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -186,6 +186,18 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host var hostIdsToReturn []string if plg != nil && opts.WithSetMembers { + cat, err := r.getCatalog(ctx, setToReturn.GetCatalogId()) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + plgCat, err := toPluginCatalog(ctx, cat) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + persisted, err := r.getPersistedDataForCatalog(ctx, cat) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } plgSet, err := toPluginSet(ctx, setToReturn) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op) @@ -195,7 +207,9 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("no plugin found for plugin id %s", plg.GetPublicId())) } resp, err := plgClient.ListHosts(ctx, &plgpb.ListHostsRequest{ + Catalog: plgCat, Sets: []*hspb.HostSet{plgSet}, + Persisted: persisted, }) switch { case err != nil: diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index ffd6dba73..1d68c05cd 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -319,7 +319,13 @@ func TestRepository_LookupSet(t *testing.T) { catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plg.GetPublicId(): &TestPluginServer{ + ListHostsFn: func(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + require.NotEmpty(t, req.GetSets()) + require.NotNil(t, req.GetCatalog()) + return &plgpb.ListHostsResponse{}, nil + }, + }, }) hostSetId, err := newHostSetId(ctx) require.NoError(t, err) diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 41d01118d..08f81fb61 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -6,18 +6,21 @@ import ( "fmt" "sync" + awsplg "github.com/hashicorp/boundary-plugin-host-aws/plugin" + azureplg "github.com/hashicorp/boundary-plugin-host-azure/plugin" "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" - hostplugin "github.com/hashicorp/boundary/internal/host/plugin" + pluginhost "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/scheduler/job" "github.com/hashicorp/boundary/internal/servers" @@ -104,6 +107,12 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } } + if _, err := conf.CreateHostPlugin(ctx, "azure", &azureplg.AzurePlugin{}, hostplugin.WithDescription("Boundary provided host plugin for Azure.")); err != nil { + return nil, fmt.Errorf("error creating azure host plugin: %w", err) + } + if _, err := conf.CreateHostPlugin(ctx, "aws", &awsplg.AwsPlugin{}, hostplugin.WithDescription("Boundary provided host plugin for AWS.")); err != nil { + return nil, fmt.Errorf("error creating aws host plugin: %w", err) + } if conf.HostPlugins == nil { conf.HostPlugins = make(map[string]plugin.HostPluginServiceServer) } @@ -140,8 +149,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.StaticHostRepoFn = func() (*static.Repository, error) { return static.NewRepository(dbase, dbase, c.kms) } - c.PluginHostRepoFn = func() (*hostplugin.Repository, error) { - return hostplugin.NewRepository(dbase, dbase, c.kms, c.conf.HostPlugins) + c.PluginHostRepoFn = func() (*pluginhost.Repository, error) { + return pluginhost.NewRepository(dbase, dbase, c.kms, c.conf.HostPlugins) } c.HostPluginRepoFn = func() (*host.Repository, error) { return host.NewRepository(dbase, dbase, c.kms) diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index a8db57fbc..7531926ea 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -472,6 +472,9 @@ func (s Service) createPluginInRepo(ctx context.Context, projId string, req *pbs if err != nil { return nil, nil, errors.Wrap(ctx, err, op) } + if plg == nil { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "plugin with provided name not found") + } pluginId = plg.GetPublicId() } h, err := toStoragePluginCatalog(ctx, projId, pluginId, item) diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index 79bfa0ed1..d4e890aa8 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/db/schema" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/intglobals" "github.com/hashicorp/boundary/internal/kms" @@ -576,6 +577,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { } } else if !opts.DisableDatabaseCreation { var createOpts []base.Option + createOpts = append(createOpts, base.WithHostPlugin("pl_1234567890", plugin.NewLoopbackPlugin())) if opts.DisableAuthMethodCreation { createOpts = append(createOpts, base.WithSkipAuthMethodCreation()) } From 08ce7ac8890dd00ed48975068a3ed75e28ff148a Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 30 Sep 2021 00:39:37 -0400 Subject: [PATCH 28/71] Update azure plugin to fix tag-based filtering --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cb2a711b9..b32a39b0b 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/google/go-cmp v0.5.6 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e github.com/hashicorp/boundary/api v0.0.18 github.com/hashicorp/boundary/sdk v0.0.11-0.20210928201932-8b1ca4b0b8b9 github.com/hashicorp/cap v0.1.1 diff --git a/go.sum b/go.sum index b74d48641..227e7245c 100644 --- a/go.sum +++ b/go.sum @@ -425,8 +425,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q8 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 h1:XrH7otQgHtqcYX1qbO10DypzjEBwYdtYcIEc9d1BG5w= github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578/go.mod h1:TbqGUjL8yDVqdoEHCiEPoE7lv5yfQRKxqPinfGUeC30= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d h1:WPcdLfDLQVhejIbvSfNp3uV5lJ42eLktqKfUpj1NP4w= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210929210209-bb97249db88d/go.mod h1:dtbUh5NNMbV9pZNncSNo3JPoG3zm9LeMBYz+lKVdv0c= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e h1:UfEvqkWfyIQ846nFeAWKnS9HAtP9xLk3NxqU3+zLeZY= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e/go.mod h1:dtbUh5NNMbV9pZNncSNo3JPoG3zm9LeMBYz+lKVdv0c= github.com/hashicorp/cap v0.1.1 h1:GjO4+9+H0wv/89YoEsxeVc2jIizL19r5v5l2lpaH8Kg= github.com/hashicorp/cap v0.1.1/go.mod h1:VfBvK2ULRyqsuqAnjgZl7HJ7/CGMC7ro4H5eXiZuun8= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= From 098c3e1f22ca59bb903a9cb78f80d279d5179835 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 5 Oct 2021 16:28:39 -0400 Subject: [PATCH 29/71] Update sdk dep from proper branch and fix building --- go.mod | 3 +-- .../migrations/postgres/17/preferred_endpoints_test.go | 2 +- internal/host/plugin/host_catalog_secret.go | 9 +++++---- internal/host/plugin/query.go | 4 ++-- internal/host/plugin/testing.go | 5 ++--- internal/plugin/host/testing.go | 3 +-- internal/plugin/testing.go | 3 +-- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 86e8fc1dd..5334d8889 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e github.com/hashicorp/boundary/api v0.0.19 - github.com/hashicorp/boundary/sdk v0.0.11-0.20210928201932-8b1ca4b0b8b9 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211005202728-158ab09952ba github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 @@ -56,7 +56,6 @@ require ( github.com/jackc/pgconn v1.10.0 github.com/jackc/pgx/v4 v4.11.0 github.com/jefferai/keyring v1.1.7-0.20210105022822-8749b3d9ce79 - github.com/jinzhu/gorm v1.9.12 github.com/kr/pretty v0.3.0 github.com/kr/text v0.2.0 github.com/mattn/go-colorable v0.1.8 diff --git a/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go b/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go index fed33e577..dc143fc92 100644 --- a/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go +++ b/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go @@ -77,7 +77,7 @@ func Test_PreferredEndpointTable(t *testing.T) { condition: "dns:abcd", }, }, - wantErrContains: "priority must not be empty", + wantErrContains: "priority_must_be_greater_than_zero", }, { testName: "duplicate priority", diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go index 83916ec02..7daf0abf3 100644 --- a/internal/host/plugin/host_catalog_secret.go +++ b/internal/host/plugin/host_catalog_secret.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "database/sql" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -92,9 +93,9 @@ func (c *HostCatalogSecret) decrypt(ctx context.Context, cipher wrapping.Wrapper func (c *HostCatalogSecret) insertQuery() (query string, queryValues []interface{}) { query = upsertHostCatalogSecretQuery queryValues = []interface{}{ - c.CatalogId, - c.CtSecret, - c.KeyId, + sql.Named("catalog_id", c.CatalogId), + sql.Named("secret", c.CtSecret), + sql.Named("key_id", c.KeyId), } return } @@ -102,7 +103,7 @@ func (c *HostCatalogSecret) insertQuery() (query string, queryValues []interface func (c *HostCatalogSecret) deleteQuery() (query string, queryValues []interface{}) { query = deleteHostCatalogSecretQuery queryValues = []interface{}{ - c.CatalogId, + sql.Named("catalog_id", c.CatalogId), } return } diff --git a/internal/host/plugin/query.go b/internal/host/plugin/query.go index bf3db9dfe..4ad81ad1a 100644 --- a/internal/host/plugin/query.go +++ b/internal/host/plugin/query.go @@ -5,7 +5,7 @@ const ( insert into host_plugin_catalog_secret (catalog_id, secret, key_id) values - ($1, $2, $3) + (@catalog_id, @secret, @key_id) on conflict (catalog_id) do update set secret = excluded.secret, key_id = excluded.key_id @@ -14,6 +14,6 @@ returning *; deleteHostCatalogSecretQuery = ` delete from host_plugin_catalog_secret - where catalog_id = $1; + where catalog_id = @catalog_id; ` ) diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index a52814fd1..b7f9a50ec 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" - "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,7 +15,7 @@ import ( // TestCatalog creates count number of plugin host catalogs to the provided DB // with the provided scope id. If any errors are encountered during the creation of // the host catalog, the test will fail. -func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...Option) *HostCatalog { +func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Option) *HostCatalog { t.Helper() ctx := context.Background() w := db.New(conn) @@ -41,7 +40,7 @@ func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...O // TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceServer, opt ...Option) *HostSet { +func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceServer, opt ...Option) *HostSet { t.Helper() require := require.New(t) ctx := context.Background() diff --git a/internal/plugin/host/testing.go b/internal/plugin/host/testing.go index 2bc6fa35d..e9a0e2b9e 100644 --- a/internal/plugin/host/testing.go +++ b/internal/plugin/host/testing.go @@ -5,11 +5,10 @@ import ( "testing" "github.com/hashicorp/boundary/internal/db" - "github.com/jinzhu/gorm" "github.com/stretchr/testify/require" ) -func TestPlugin(t *testing.T, conn *gorm.DB, name string) *Plugin { +func TestPlugin(t *testing.T, conn *db.DB, name string) *Plugin { t.Helper() p := NewPlugin(WithName(name)) id, err := newPluginId() diff --git a/internal/plugin/testing.go b/internal/plugin/testing.go index 01d5a3929..01b6efca5 100644 --- a/internal/plugin/testing.go +++ b/internal/plugin/testing.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/plugin/store" "github.com/hashicorp/boundary/internal/types/scope" - "github.com/jinzhu/gorm" "github.com/stretchr/testify/require" ) @@ -42,7 +41,7 @@ func (c *plugin) SetTableName(n string) { c.tableName = n } -func testPlugin(t *testing.T, conn *gorm.DB, name string) *plugin { +func testPlugin(t *testing.T, conn *db.DB, name string) *plugin { t.Helper() p := newPlugin(name) id, err := db.NewPublicId("plg") From 70e653691987f1e6365b9ba634cf7b4d56a55e5f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 7 Oct 2021 11:56:39 -0400 Subject: [PATCH 30/71] Swap server interface for client (#1580) --- internal/cmd/base/initial_resources.go | 4 +- internal/cmd/base/option.go | 6 +-- internal/cmd/base/servers.go | 2 +- internal/cmd/commands/dev/dev.go | 2 +- internal/host/plugin/client.go | 48 +++++++++++++++++++ internal/host/plugin/immutable_fields_test.go | 4 +- internal/host/plugin/repository.go | 6 +-- .../plugin/repository_host_catalog_test.go | 22 +++++---- internal/host/plugin/repository_host_set.go | 6 +-- .../host/plugin/repository_host_set_test.go | 32 ++++++------- internal/host/plugin/repository_test.go | 4 +- internal/host/plugin/testing.go | 2 +- internal/host/plugin/testing_test.go | 2 +- internal/host/preferred_endpoint_test.go | 8 ++-- internal/servers/controller/controller.go | 6 +-- .../host_catalog_service_test.go | 20 ++++---- .../host_sets/host_set_service_test.go | 32 ++++++------- .../handlers/targets/target_service_test.go | 34 ++++++------- internal/servers/controller/testing.go | 2 +- 19 files changed, 146 insertions(+), 96 deletions(-) create mode 100644 internal/host/plugin/client.go diff --git a/internal/cmd/base/initial_resources.go b/internal/cmd/base/initial_resources.go index 799f1ae10..d9c7fb211 100644 --- a/internal/cmd/base/initial_resources.go +++ b/internal/cmd/base/initial_resources.go @@ -591,7 +591,7 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) // It also registers the plugin in the shared map of running plugins. Since // all boundary provided host plugins must have a name, a name is required // when calling CreateHostPlugin and will be used even if WithName is provided. -func (b *Server) CreateHostPlugin(ctx context.Context, name string, plg plgpb.HostPluginServiceServer, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { +func (b *Server) CreateHostPlugin(ctx context.Context, name string, plg plgpb.HostPluginServiceClient, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { if name == "" { return nil, fmt.Errorf("no name provided when creating plugin.") } @@ -641,7 +641,7 @@ func (b *Server) CreateHostPlugin(ctx context.Context, name string, plg plgpb.Ho } if b.HostPlugins == nil { - b.HostPlugins = make(map[string]plgpb.HostPluginServiceServer) + b.HostPlugins = make(map[string]plgpb.HostPluginServiceClient) } b.HostPlugins[plugin.GetPublicId()] = plg diff --git a/internal/cmd/base/option.go b/internal/cmd/base/option.go index 96a164739..de946202a 100644 --- a/internal/cmd/base/option.go +++ b/internal/cmd/base/option.go @@ -36,7 +36,7 @@ type Options struct { withEventFlags *EventFlags withAttributeFieldPrefix string withStatusCode int - withHostPlugin func() (string, plugin.HostPluginServiceServer) + withHostPlugin func() (string, plugin.HostPluginServiceClient) } func getDefaultOptions() Options { @@ -165,9 +165,9 @@ func WithDatabaseTemplate(template string) Option { // WithHostPlugin allows specifying a plugin ID and implementation to create at // startup -func WithHostPlugin(pluginId string, plg plugin.HostPluginServiceServer) Option { +func WithHostPlugin(pluginId string, plg plugin.HostPluginServiceClient) Option { return func(o *Options) { - o.withHostPlugin = func() (string, plugin.HostPluginServiceServer) { + o.withHostPlugin = func() (string, plugin.HostPluginServiceClient) { return pluginId, plg } } diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 6d04a0078..26d839fcf 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -114,7 +114,7 @@ type Server struct { DevTargetSessionConnectionLimit int DevLoopbackHostPluginId string - HostPlugins map[string]plgpb.HostPluginServiceServer + HostPlugins map[string]plgpb.HostPluginServiceClient DevOidcSetup oidcSetup diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index eb062a9a2..5bdc01e70 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -551,7 +551,7 @@ func (c *Command) Run(args []string) int { var opts []base.Option if c.flagCreateLoopbackHostCatalogPlugin { - opts = append(opts, base.WithHostPlugin("pl_1234567890", plugin.NewLoopbackPlugin())) + opts = append(opts, base.WithHostPlugin("pl_1234567890", plugin.NewWrappingPluginClient(plugin.NewLoopbackPlugin()))) } switch c.flagDatabaseUrl { case "": diff --git a/internal/host/plugin/client.go b/internal/host/plugin/client.go new file mode 100644 index 000000000..7c2d3271f --- /dev/null +++ b/internal/host/plugin/client.go @@ -0,0 +1,48 @@ +package plugin + +import ( + "context" + + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "google.golang.org/grpc" +) + +var _ plgpb.HostPluginServiceClient = (*WrappingPluginClient)(nil) + +// WrappingPluginClient provides a wrapper around a Server implementation that +// can be used when loading a plugin in-memory +type WrappingPluginClient struct { + Server plgpb.HostPluginServiceServer +} + +func NewWrappingPluginClient(s plgpb.HostPluginServiceServer) *WrappingPluginClient { + return &WrappingPluginClient{Server: s} +} + +func (tpc *WrappingPluginClient) OnCreateCatalog(ctx context.Context, req *plgpb.OnCreateCatalogRequest, opts ...grpc.CallOption) (*plgpb.OnCreateCatalogResponse, error) { + return tpc.Server.OnCreateCatalog(ctx, req) +} + +func (tpc *WrappingPluginClient) OnUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest, opts ...grpc.CallOption) (*plgpb.OnUpdateCatalogResponse, error) { + return tpc.Server.OnUpdateCatalog(ctx, req) +} + +func (tpc *WrappingPluginClient) OnDeleteCatalog(ctx context.Context, req *plgpb.OnDeleteCatalogRequest, opts ...grpc.CallOption) (*plgpb.OnDeleteCatalogResponse, error) { + return tpc.Server.OnDeleteCatalog(ctx, req) +} + +func (tpc *WrappingPluginClient) OnCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest, opts ...grpc.CallOption) (*plgpb.OnCreateSetResponse, error) { + return tpc.Server.OnCreateSet(ctx, req) +} + +func (tpc *WrappingPluginClient) OnUpdateSet(ctx context.Context, req *plgpb.OnUpdateSetRequest, opts ...grpc.CallOption) (*plgpb.OnUpdateSetResponse, error) { + return tpc.Server.OnUpdateSet(ctx, req) +} + +func (tpc *WrappingPluginClient) OnDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest, opts ...grpc.CallOption) (*plgpb.OnDeleteSetResponse, error) { + return tpc.Server.OnDeleteSet(ctx, req) +} + +func (tpc *WrappingPluginClient) ListHosts(ctx context.Context, req *plgpb.ListHostsRequest, opts ...grpc.CallOption) (*plgpb.ListHostsResponse, error) { + return tpc.Server.ListHosts(ctx, req) +} diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index 68f37ee36..e3ea66832 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -102,8 +102,8 @@ func TestPluginSet_ImmutableFields(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), }) tests := []struct { diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index f52d98572..e40a756bd 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -22,7 +22,7 @@ type Repository struct { // // TODO: When we are using go-plugin change from using a // plgpb.HostPluginServiceServer to the client. - plugins map[string]plgpb.HostPluginServiceServer + plugins map[string]plgpb.HostPluginServiceClient // defaultLimit provides a default for limiting the number of results // returned from the repo defaultLimit int @@ -32,7 +32,7 @@ type Repository struct { // only be used for one transaction and it is not safe for concurrent go // routines to access it. WithLimit option is used as a repo wide default // limit applied to all ListX methods. -func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceServer, opt ...host.Option) (*Repository, error) { +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient, opt ...host.Option) (*Repository, error) { const op = "plugin.NewRepository" switch { case r == nil: @@ -54,7 +54,7 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb opts.WithLimit = db.DefaultLimit } - plgs := make(map[string]plgpb.HostPluginServiceServer, len(plgm)) + plgs := make(map[string]plgpb.HostPluginServiceClient, len(plgm)) for k, v := range plgm { plgs[k] = v } diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 87b6664f4..58993a303 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -34,14 +34,16 @@ func TestRepository_CreateCatalog(t *testing.T) { // gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the // test against the expected values sent to the plugin. var gotPluginAttrs *structpb.Struct - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{ - OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { - gotPluginAttrs = req.GetCatalog().GetAttributes() - return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + gotPluginAttrs = req.GetCatalog().GetAttributes() + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil + }, }, }, - unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + unimplementedPlugin.GetPublicId(): &WrappingPluginClient{Server: &plgpb.UnimplementedHostPluginServiceServer{}}, } tests := []struct { @@ -378,8 +380,8 @@ func TestRepository_LookupCatalog(t *testing.T) { wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{Server: &TestPluginServer{}}, } cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) badId, err := newHostCatalogId(ctx) @@ -444,8 +446,8 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) plg := hostplg.TestPlugin(t, conn, "test") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{Server: &TestPluginServer{}}, } repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(t, err) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index bc13adc0e..63d6f6e44 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -207,8 +207,8 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("no plugin found for plugin id %s", plg.GetPublicId())) } resp, err := plgClient.ListHosts(ctx, &plgpb.ListHostsRequest{ - Catalog: plgCat, - Sets: []*hspb.HostSet{plgSet}, + Catalog: plgCat, + Sets: []*hspb.HostSet{plgSet}, Persisted: persisted, }) switch { @@ -436,7 +436,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En type catalogInfo struct { publicId string // ID of the catalog - plg plgpb.HostPluginServiceServer // plugin client for the catalog + plg plgpb.HostPluginServiceClient // plugin client for the catalog setInfos map[string]*setInfo // map of set IDs to set information plgCat *hcpb.HostCatalog // storage host catalog persisted *plgpb.HostCatalogPersisted // host catalog persisted (secret) data diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 1d68c05cd..9d177a001 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -36,12 +36,12 @@ func TestRepository_CreateSet(t *testing.T) { unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") var pluginReceivedAttrs *structpb.Struct - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { pluginReceivedAttrs = req.GetSet().GetAttributes() return &plgpb.OnCreateSetResponse{}, nil - }}, - unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + }}), + unimplementedPlugin.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) @@ -313,19 +313,19 @@ func TestRepository_LookupSet(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "lookup") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{ + hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{ ListHostsFn: func(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { require.NotEmpty(t, req.GetSets()) require.NotNil(t, req.GetCatalog()) return &plgpb.ListHostsResponse{}, nil }, - }, + }), }) hostSetId, err := newHostSetId(ctx) require.NoError(t, err) @@ -383,8 +383,8 @@ func TestRepository_Endpoints(t *testing.T) { plg := hostplg.TestPlugin(t, conn, "endpoints") hostlessCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{ + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{ ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { if req.Catalog.GetId() == hostlessCatalog.GetPublicId() { return &plgpb.ListHostsResponse{}, nil @@ -402,7 +402,7 @@ func TestRepository_Endpoints(t *testing.T) { }, }}, nil }, - }, + }), } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) @@ -514,8 +514,8 @@ func TestRepository_ListSets(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "list") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), } catalogA := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) @@ -586,8 +586,8 @@ func TestRepository_ListSets_Limits(t *testing.T) { _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "listlimit") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) count := 10 diff --git a/internal/host/plugin/repository_test.go b/internal/host/plugin/repository_test.go index 22140d143..dc4e32b09 100644 --- a/internal/host/plugin/repository_test.go +++ b/internal/host/plugin/repository_test.go @@ -19,13 +19,13 @@ func TestRepository_New(t *testing.T) { wrapper := db.TestWrapper(t) kmsCache := kms.TestKms(t, conn, wrapper) - plgs := map[string]plgpb.HostPluginServiceServer{} + plgs := map[string]plgpb.HostPluginServiceClient{} type args struct { r db.Reader w db.Writer kms *kms.Kms - plugins map[string]plgpb.HostPluginServiceServer + plugins map[string]plgpb.HostPluginServiceClient opts []host.Option } diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index b7f9a50ec..a4159e724 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -40,7 +40,7 @@ func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Opt // TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceServer, opt ...Option) *HostSet { +func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) *HostSet { t.Helper() require := require.New(t) ctx := context.Background() diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index 713869669..f74526c63 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -45,7 +45,7 @@ func Test_TestSet(t *testing.T) { assert.NotEmpty(plg.GetPublicId()) c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) - set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceServer{plg.GetPublicId(): &TestPluginServer{}}, WithName("foo"), WithDescription("bar")) + set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceClient{plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{})}, WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) db.AssertPublicId(t, HostSetPrefix, set.GetPublicId()) assert.Equal("foo", set.GetName()) diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go index 738a16b40..e58700bd0 100644 --- a/internal/host/preferred_endpoint_test.go +++ b/internal/host/preferred_endpoint_test.go @@ -27,8 +27,8 @@ func TestPreferredEndpoint_Create(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) type args struct { @@ -157,8 +157,8 @@ func TestPreferredEndpoint_Delete(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{}, + set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), }) peFunc := func(priority uint32, condition string) *host.PreferredEndpoint { diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 08f81fb61..4c589f874 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -107,14 +107,14 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } } - if _, err := conf.CreateHostPlugin(ctx, "azure", &azureplg.AzurePlugin{}, hostplugin.WithDescription("Boundary provided host plugin for Azure.")); err != nil { + if _, err := conf.CreateHostPlugin(ctx, "azure", pluginhost.NewWrappingPluginClient(&azureplg.AzurePlugin{}), hostplugin.WithDescription("Boundary provided host plugin for Azure.")); err != nil { return nil, fmt.Errorf("error creating azure host plugin: %w", err) } - if _, err := conf.CreateHostPlugin(ctx, "aws", &awsplg.AwsPlugin{}, hostplugin.WithDescription("Boundary provided host plugin for AWS.")); err != nil { + if _, err := conf.CreateHostPlugin(ctx, "aws", pluginhost.NewWrappingPluginClient(&awsplg.AwsPlugin{}), hostplugin.WithDescription("Boundary provided host plugin for AWS.")); err != nil { return nil, fmt.Errorf("error creating aws host plugin: %w", err) } if conf.HostPlugins == nil { - conf.HostPlugins = make(map[string]plugin.HostPluginServiceServer) + conf.HostPlugins = make(map[string]plugin.HostPluginServiceClient) } // Set up repo stuff diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index a0f496094..01650a4f3 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -74,7 +74,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -163,7 +163,7 @@ func TestGet_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -262,7 +262,7 @@ func TestList(t *testing.T) { return iam.TestRepo(t, conn, wrapper), nil } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -467,7 +467,7 @@ func TestDelete(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -537,7 +537,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -572,7 +572,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -732,12 +732,12 @@ func TestCreate_Plugin(t *testing.T) { name := "test" plg := host.TestPlugin(t, conn, name) pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{ + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{ OnCreateCatalogFn: func(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { return nil, nil }, - }, + }), }) } defaultHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) @@ -899,7 +899,7 @@ func TestUpdate(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 5e0ac56fa..6fd73e248 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -57,7 +57,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -153,8 +153,8 @@ func TestGet_Plugin(t *testing.T) { name := "test" prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} plg := hostplugin.TestPlugin(t, conn, name) - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), } pluginRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, plgm) @@ -253,7 +253,7 @@ func TestList_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2) hc, hcNoHosts := hcs[0], hcs[1] @@ -352,8 +352,8 @@ func TestList_Plugin(t *testing.T) { } name := "test" plg := hostplugin.TestPlugin(t, conn, name) - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), } pluginRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, plgm) @@ -463,7 +463,7 @@ func TestDelete(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -542,7 +542,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -578,7 +578,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -723,12 +723,12 @@ func TestCreate_Plugin(t *testing.T) { name := "test" plg := hostplugin.TestPlugin(t, conn, name) plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{ + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{ OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { return nil, nil }, - }, + }), }) } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) @@ -1002,7 +1002,7 @@ func TestUpdate(t *testing.T) { } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } tested, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Failed to create a new host set service.") @@ -1352,7 +1352,7 @@ func TestAddHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1472,7 +1472,7 @@ func TestSetHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1588,7 +1588,7 @@ func TestRemoveHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 85b48fd55..778b208b1 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -88,7 +88,7 @@ func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapp return static.NewRepository(rw, rw, kms) } pluginHostRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) @@ -986,8 +986,8 @@ func TestAddTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) addCases := []struct { @@ -1120,8 +1120,8 @@ func TestSetTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) setCases := []struct { @@ -1248,8 +1248,8 @@ func TestRemoveTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) removeCases := []struct { @@ -1388,8 +1388,8 @@ func TestAddTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) addCases := []struct { @@ -1522,8 +1522,8 @@ func TestSetTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) setCases := []struct { @@ -1644,8 +1644,8 @@ func TestRemoveTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): &plugin.TestPluginServer{}, + pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) removeCases := []struct { @@ -2589,8 +2589,8 @@ func TestAuthorizeSession(t *testing.T) { } plg := host.TestPlugin(t, conn, "test") - plgm := map[string]plgpb.HostPluginServiceServer{ - plg.GetPublicId(): plugin.TestPluginServer{ + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(plugin.TestPluginServer{ ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { var setIds []string for _, set := range req.GetSets() { @@ -2609,7 +2609,7 @@ func TestAuthorizeSession(t *testing.T) { }, }}, nil }, - }, + }), } pluginHostRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, plgm) @@ -2814,7 +2814,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{}) + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index fea334a2a..247c6bc97 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -577,7 +577,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { } } else if !opts.DisableDatabaseCreation { var createOpts []base.Option - createOpts = append(createOpts, base.WithHostPlugin("pl_1234567890", plugin.NewLoopbackPlugin())) + createOpts = append(createOpts, base.WithHostPlugin("pl_1234567890", plugin.NewWrappingPluginClient(plugin.NewLoopbackPlugin()))) if opts.DisableAuthMethodCreation { createOpts = append(createOpts, base.WithSkipAuthMethodCreation()) } From 2a40a734aa30c732d8ee6b4bcf59b11680824024 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 8 Oct 2021 21:02:29 -0400 Subject: [PATCH 31/71] Update preferred endpoint preference order (#1583) Rather than pick addresses totally at random, this changes the preference to be private IP4 > public IP4 > DNS. (Ordering is not guaranteed, but in many cases there will only be one of each type of IP anyways.) All three of these are preferred over IP6, which also goes public > private. This seems likely to match the common case where you're getting into a machine on the private side from the public side. It's harder to know if DNS is private or not, so IPs are preferred in this case. --- internal/libs/endpoint/preferencer.go | 53 +++++++++++++++-- internal/libs/endpoint/preferencer_test.go | 68 +++++++++++++++++----- 2 files changed, 102 insertions(+), 19 deletions(-) diff --git a/internal/libs/endpoint/preferencer.go b/internal/libs/endpoint/preferencer.go index 44619fd80..ab838855a 100644 --- a/internal/libs/endpoint/preferencer.go +++ b/internal/libs/endpoint/preferencer.go @@ -2,8 +2,7 @@ package endpoint import ( "context" - "math/rand" - "time" + "net" "github.com/hashicorp/boundary/internal/errors" ) @@ -47,10 +46,52 @@ func (p *preferencer) Choose(ctx context.Context, opt ...Option) (string, error) switch len(p.matchers) { case 0: - // We have no matchers, so pick one at random - allAddrs := append(opts.withIpAddrs, opts.withDnsNames...) - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - return allAddrs[rng.Intn(len(allAddrs))], nil + // We have no matchers, so pick a private address if we have one (since + // those are most likely to be what the user is trying to gain access to + // via a worker). If we don't have one, pick any IP address, as that's + // also more likely desired than a DNS name. Otherwise, pick a DNS name. + // IPv6 follows same rules as 4, but least preferenced. + nonPrivateIp4s := make([]string, 0, len(opts.withIpAddrs)) + nonPrivateIp6s := make([]string, 0, len(opts.withIpAddrs)) + privateIp6s := make([]string, 0, len(opts.withIpAddrs)) + + for _, ipStr := range opts.withIpAddrs { + ipVal := net.ParseIP(ipStr) + if ipVal != nil { + switch ipVal.To4() { + case nil: // it's v6 + if ipVal.IsPrivate() { + privateIp6s = append(privateIp6s, ipStr) + } else { + nonPrivateIp6s = append(nonPrivateIp6s, ipStr) + } + default: + if ipVal.IsPrivate() { + // private IPv4 is most highly preferenced, so return it directly + return ipStr, nil + } + nonPrivateIp4s = append(nonPrivateIp4s, ipStr) + } + } + } + + switch { + case len(nonPrivateIp4s) > 0: + return nonPrivateIp4s[0], nil + + case len(opts.withDnsNames) > 0: + return opts.withDnsNames[0], nil + + case len(privateIp6s) > 0: + return privateIp6s[0], nil + + case len(nonPrivateIp6s) > 0: + return nonPrivateIp6s[0], nil + + default: + // We literally have nothing if we get here, so return nothing + return "", nil + } default: for _, m := range p.matchers { diff --git a/internal/libs/endpoint/preferencer_test.go b/internal/libs/endpoint/preferencer_test.go index 917a75aa8..0fd904a60 100644 --- a/internal/libs/endpoint/preferencer_test.go +++ b/internal/libs/endpoint/preferencer_test.go @@ -91,20 +91,62 @@ func TestPreferencer(t *testing.T) { }) } }) - t.Run("noPrefRandom", func(t *testing.T) { + t.Run("noPrefReturnsPrivate", func(t *testing.T) { + const privAddr = "192.168.4.3" p, err := NewPreferencer(ctx) require.NoError(t, err) - checkMap := map[string]int{} - for i := 0; i < 200; i++ { - out, err := p.Choose( - ctx, - WithIpAddrs([]string{"48.134.5.1", "1.2.7.56"}), - WithDnsNames([]string{"foo.bar.com", "bar.baz.com"}), - ) - require.NoError(t, err) - checkMap[out] = checkMap[out] + 1 - } - // Ensure that we've inserted all four keys - assert.Len(t, checkMap, 4) + out, err := p.Choose( + ctx, + WithIpAddrs([]string{"48.134.5.1", "2001::1", privAddr}), + WithDnsNames([]string{"foo.bar.com", "bar.baz.com"}), + ) + require.NoError(t, err) + assert.Equal(t, privAddr, out) + }) + t.Run("noPrefNoPrivateReturnsIp4", func(t *testing.T) { + const exp = "48.134.5.1" + p, err := NewPreferencer(ctx) + require.NoError(t, err) + out, err := p.Choose( + ctx, + WithIpAddrs([]string{"2001::1", exp}), + WithDnsNames([]string{"foo.bar.com", "bar.baz.com"}), + ) + require.NoError(t, err) + assert.Equal(t, exp, out) + }) + t.Run("noPrefNoIp4ReturnsDns", func(t *testing.T) { + const exp = "foo.bar.com" + p, err := NewPreferencer(ctx) + require.NoError(t, err) + out, err := p.Choose( + ctx, + WithIpAddrs([]string{"2001::1"}), + WithDnsNames([]string{exp}), + ) + require.NoError(t, err) + assert.Equal(t, exp, out) + }) + t.Run("ip6OnlyReturnsPrivate", func(t *testing.T) { + const exp = "fc00::1" + p, err := NewPreferencer(ctx) + require.NoError(t, err) + out, err := p.Choose( + ctx, + WithIpAddrs([]string{"2001::1", exp}), + ) + require.NoError(t, err) + assert.Equal(t, exp, out) + }) + t.Run("ip6OnlyPublic", func(t *testing.T) { + const exp = "2001::1" + p, err := NewPreferencer(ctx) + require.NoError(t, err) + out, err := p.Choose( + ctx, + WithIpAddrs([]string{exp}), + ) + require.NoError(t, err) + assert.Equal(t, exp, out) }) } From 2baa5d399936d7cfda348cea7f873f13b04acbe8 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Oct 2021 12:40:29 -0400 Subject: [PATCH 32/71] Move postgres files post-merge from main --- .../db/schema/migrations/{ => oss}/postgres/17/01_plugin.up.sql | 0 .../migrations/{ => oss}/postgres/17/02_hostplugin.up.sql | 0 .../db/schema/migrations/{ => oss}/postgres/17/03_host.up.sql | 0 .../migrations/{ => oss}/postgres/17/04_plugin_host.up.sql | 0 .../{ => oss}/postgres/17/05_preferred_endpoints.up.sql | 0 .../{ => oss}/postgres/17/06_wh_session_dimensions.up.sql | 0 .../preferred_endpoints_test.go => oss/postgres_17_05_test.go} | 2 +- 7 files changed, 1 insertion(+), 1 deletion(-) rename internal/db/schema/migrations/{ => oss}/postgres/17/01_plugin.up.sql (100%) rename internal/db/schema/migrations/{ => oss}/postgres/17/02_hostplugin.up.sql (100%) rename internal/db/schema/migrations/{ => oss}/postgres/17/03_host.up.sql (100%) rename internal/db/schema/migrations/{ => oss}/postgres/17/04_plugin_host.up.sql (100%) rename internal/db/schema/migrations/{ => oss}/postgres/17/05_preferred_endpoints.up.sql (100%) rename internal/db/schema/migrations/{ => oss}/postgres/17/06_wh_session_dimensions.up.sql (100%) rename internal/db/schema/migrations/{postgres/17/preferred_endpoints_test.go => oss/postgres_17_05_test.go} (99%) diff --git a/internal/db/schema/migrations/postgres/17/01_plugin.up.sql b/internal/db/schema/migrations/oss/postgres/17/01_plugin.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/01_plugin.up.sql rename to internal/db/schema/migrations/oss/postgres/17/01_plugin.up.sql diff --git a/internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql b/internal/db/schema/migrations/oss/postgres/17/02_hostplugin.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/02_hostplugin.up.sql rename to internal/db/schema/migrations/oss/postgres/17/02_hostplugin.up.sql diff --git a/internal/db/schema/migrations/postgres/17/03_host.up.sql b/internal/db/schema/migrations/oss/postgres/17/03_host.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/03_host.up.sql rename to internal/db/schema/migrations/oss/postgres/17/03_host.up.sql diff --git a/internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/17/04_plugin_host.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/04_plugin_host.up.sql rename to internal/db/schema/migrations/oss/postgres/17/04_plugin_host.up.sql diff --git a/internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/17/05_preferred_endpoints.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/05_preferred_endpoints.up.sql rename to internal/db/schema/migrations/oss/postgres/17/05_preferred_endpoints.up.sql diff --git a/internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql b/internal/db/schema/migrations/oss/postgres/17/06_wh_session_dimensions.up.sql similarity index 100% rename from internal/db/schema/migrations/postgres/17/06_wh_session_dimensions.up.sql rename to internal/db/schema/migrations/oss/postgres/17/06_wh_session_dimensions.up.sql diff --git a/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go b/internal/db/schema/migrations/oss/postgres_17_05_test.go similarity index 99% rename from internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go rename to internal/db/schema/migrations/oss/postgres_17_05_test.go index dc143fc92..662270538 100644 --- a/internal/db/schema/migrations/postgres/17/preferred_endpoints_test.go +++ b/internal/db/schema/migrations/oss/postgres_17_05_test.go @@ -1,4 +1,4 @@ -package migration +package oss_test import ( "context" From 1c8e0ed1e1cb041944034264fb22b5537d69eb7e Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 14 Oct 2021 11:56:08 -0400 Subject: [PATCH 33/71] Add scaffolding for external host plugins and convert aws/azure over (#1581) --- .circleci/config.yml | 213 +++-- .circleci/config/@build-release.yml | 208 ++-- .gitignore | 1 + Makefile | 8 +- go.mod | 13 +- go.sum | 22 +- internal/cmd/base/dev.go | 2 +- internal/cmd/base/initial_resources.go | 6 +- internal/cmd/commands/database/migrate.go | 40 + internal/cmd/commands/dev/dev.go | 30 +- internal/cmd/config/config.go | 7 + internal/cmd/config/config_test.go | 13 + internal/servers/controller/controller.go | 34 +- .../controller/handlers/subtypes_test.go | 2 +- packages-oss.lock/pkgs.yml | 165 ++-- packages-oss.yml | 14 + plugins/README.md | 6 + plugins/host/assets.go | 21 + plugins/host/mains/aws/go.mod | 17 + plugins/host/mains/aws/go.sum | 891 ++++++++++++++++++ plugins/host/mains/aws/main.go | 18 + plugins/host/mains/azure/go.mod | 10 + plugins/host/mains/azure/go.sum | 874 +++++++++++++++++ plugins/host/mains/azure/main.go | 18 + scripts/build.sh | 94 +- sdk/go.mod | 2 + sdk/go.sum | 4 + sdk/plugins/host/load.go | 213 +++++ sdk/plugins/host/options.go | 119 +++ sdk/plugins/host/plugin.go | 89 ++ 30 files changed, 2860 insertions(+), 294 deletions(-) create mode 100644 plugins/README.md create mode 100644 plugins/host/assets.go create mode 100644 plugins/host/mains/aws/go.mod create mode 100644 plugins/host/mains/aws/go.sum create mode 100644 plugins/host/mains/aws/main.go create mode 100644 plugins/host/mains/azure/go.mod create mode 100644 plugins/host/mains/azure/go.sum create mode 100644 plugins/host/mains/azure/main.go create mode 100644 sdk/plugins/host/load.go create mode 100644 sdk/plugins/host/options.go create mode 100644 sdk/plugins/host/plugin.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 1641f8a2d..0614f6975 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,49 +30,52 @@ jobs: command: mkdir -p .buildcache && echo "*" > .buildcache/.gitignore name: Ignore .buildcache - restore_cache: - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} name: Restore package cache - restore_cache: - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} name: Restore package cache - restore_cache: - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Restore package cache - restore_cache: - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Restore package cache - restore_cache: - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Restore package cache - restore_cache: - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Restore package cache - restore_cache: - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Restore package cache - restore_cache: - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Restore package cache - restore_cache: - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Restore package cache - restore_cache: - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Restore package cache - restore_cache: - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Restore package cache - restore_cache: - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Restore package cache - restore_cache: - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Restore package cache - restore_cache: - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Restore package cache - restore_cache: - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} + name: Restore package cache + - restore_cache: + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Restore package cache - run: command: make package-meta-all @@ -84,20 +87,20 @@ jobs: command: ls -lahR .buildcache name: List Build Cache - run: - command: cp packages*.lock/pkgs.yml lockfile-d1ed8558d55be3da.yml + command: cp packages*.lock/pkgs.yml lockfile-87386c0c55d5c61a.yml name: Update Lockfile Name - run: - command: tar -czf packages-d1ed8558d55be3da.tar.gz .buildcache/packages lockfile-d1ed8558d55be3da.yml + command: tar -czf packages-87386c0c55d5c61a.tar.gz .buildcache/packages lockfile-87386c0c55d5c61a.yml name: Create Raw Package Tarball - run: - command: tar -czf meta-d1ed8558d55be3da.tar.gz .buildcache/packages/store/*.json lockfile-d1ed8558d55be3da.yml + command: tar -czf meta-87386c0c55d5c61a.tar.gz .buildcache/packages/store/*.json lockfile-87386c0c55d5c61a.yml name: Create Metadata Tarball - store_artifacts: - path: lockfile-d1ed8558d55be3da.yml + path: lockfile-87386c0c55d5c61a.yml - store_artifacts: - path: packages-d1ed8558d55be3da.tar.gz + path: packages-87386c0c55d5c61a.tar.gz - store_artifacts: - path: meta-d1ed8558d55be3da.tar.gz + path: meta-87386c0c55d5c61a.tar.gz - store_artifacts: path: .buildcache/packages environment: @@ -112,7 +115,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: a7184da6d6c92fdd0f1b0f68cb133f0b2325623f + - PACKAGE_SPEC_ID: fa8840f2e78bfa9c58d242426d80f4e30464516f steps: - setup_remote_docker: docker_layer_caching: false @@ -126,7 +129,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} name: Restore package cache - run: command: |2 @@ -172,7 +175,79 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} + name: Save package cache + paths: + - .buildcache/packages/store + darwin_arm64_package: + docker: + - image: docker.mirror.hashicorp.services/circleci/buildpack-deps + shell: /usr/bin/env bash -euo pipefail -c + environment: + - AUTO_INSTALL_TOOLS: 'YES' + - BUILDKIT_PROGRESS: plain + - PRODUCT_REVISION: '' + - PACKAGE_SPEC_ID: 110a848ce5db07e7fc389430350c45261d2d7baa + steps: + - setup_remote_docker: + docker_layer_caching: false + version: 19.03.12 + - add_ssh_keys: + fingerprints: + - 0e:03:77:f4:e2:c3:56:c2:53:6a:03:e1:31:91:2f:06 + - checkout: + path: . + - run: + command: make -C packages*.lock write-package-cache-key + name: Write package cache key + - restore_cache: + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} + name: Restore package cache + - run: + command: |2 + + if ! { PKG=$(find .buildcache/packages/store -maxdepth 1 -mindepth 1 -name '*.zip' 2> /dev/null) && [ -n "$PKG" ]; }; then + echo "No package found, continuing with build." + exit 0 + fi + echo "Package already cached, skipping build." + circleci-agent step halt + name: Check cache status + - run: + command: make -C packages*.lock write-builder-cache-keys + name: Write builder layer cache keys + - restore_cache: + key: copy-source_5c01_{{checksum ".buildcache/cache-keys/copy-source-5c01da64a31c9bfe7ba38f51d95e8bf99c0842f5"}} + keys: + - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} + - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} + - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} + name: 'Restore Builder Image Cache: copy-source' + - run: + command: make -C packages*.lock load-builder-cache + name: Load whatever builder cache we have (if any) into the Docker daemon + no_output_timeout: 30m + - run: + command: |2- + + KEYFILE="$HOME/.ssh/id_rsa_c6969882dc046c39ddac8305e3151c98" + eval "$(ssh-agent -s)" + if [ -f "$KEYFILE" ]; then + ssh-add "$KEYFILE" + else + echo "==> INFO: SSH key for github.com not found" + echo " Attempts to access private repositories from within" + echo " the build will fail, e.g. for private go modules, or" + echo " attempts to directly clone private repositories." + fi + + make -C packages*.lock package + name: Compile Package + - run: + command: ls -lahR .buildcache/packages + name: List packages + - save_cache: + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} name: Save package cache paths: - .buildcache/packages/store @@ -184,7 +259,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 4a9e88daa25103b845842c687fd8f6900e70f9a7 + - PACKAGE_SPEC_ID: e3454f0b7ee22d489b83f6daf78d545f4878a79c steps: - setup_remote_docker: docker_layer_caching: false @@ -198,7 +273,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} name: Restore package cache - run: command: |2 @@ -244,7 +319,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} name: Save package cache paths: - .buildcache/packages/store @@ -380,7 +455,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: c249aa18d9172af5dee394ffc56f6d5afc9b9470 + - PACKAGE_SPEC_ID: 276415ef2d0ac06fb080dd55910bb71613f9c049 steps: - setup_remote_docker: docker_layer_caching: false @@ -394,7 +469,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Restore package cache - run: command: |2 @@ -440,7 +515,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Save package cache paths: - .buildcache/packages/store @@ -452,7 +527,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e + - PACKAGE_SPEC_ID: 6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4 steps: - setup_remote_docker: docker_layer_caching: false @@ -466,7 +541,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Restore package cache - run: command: |2 @@ -512,7 +587,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Save package cache paths: - .buildcache/packages/store @@ -535,7 +610,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 41b52d0b959e71aaac5d843b6329d0e762b29dd9 + - PACKAGE_SPEC_ID: bced2a380b5354bba95ec9da4a54d06bc0a9b56d steps: - setup_remote_docker: docker_layer_caching: false @@ -549,7 +624,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Restore package cache - run: command: |2 @@ -595,7 +670,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Save package cache paths: - .buildcache/packages/store @@ -607,7 +682,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 51de04f17d8bd90f98ee7e90d29f7196114e9312 + - PACKAGE_SPEC_ID: 36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d steps: - setup_remote_docker: docker_layer_caching: false @@ -621,7 +696,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Restore package cache - run: command: |2 @@ -667,7 +742,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Save package cache paths: - .buildcache/packages/store @@ -679,7 +754,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: a5b1302c80e9d70de2b213f330bf0ad0055c8b96 + - PACKAGE_SPEC_ID: 0922d907d014ef141526a2a3b9d54a3940adcbf3 steps: - setup_remote_docker: docker_layer_caching: false @@ -693,7 +768,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Restore package cache - run: command: |2 @@ -739,7 +814,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Save package cache paths: - .buildcache/packages/store @@ -751,7 +826,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 89b451e85a285500ed558e0fbc7f1838ae50c040 + - PACKAGE_SPEC_ID: 1c8250fee830f8fac2364e7d3e3f82ee5832983e steps: - setup_remote_docker: docker_layer_caching: false @@ -765,7 +840,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Restore package cache - run: command: |2 @@ -811,7 +886,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Save package cache paths: - .buildcache/packages/store @@ -823,7 +898,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: a9f7f1be2569aac16eeb21967c3a47f4a04a118e + - PACKAGE_SPEC_ID: 9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f steps: - setup_remote_docker: docker_layer_caching: false @@ -837,7 +912,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Restore package cache - run: command: |2 @@ -883,7 +958,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Save package cache paths: - .buildcache/packages/store @@ -906,7 +981,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 505ce51e7cd78758af5b578825ba3395630bb1cf + - PACKAGE_SPEC_ID: ee6ae4147a76888401e2032a2fee0926ef36ad00 steps: - setup_remote_docker: docker_layer_caching: false @@ -920,7 +995,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Restore package cache - run: command: |2 @@ -966,7 +1041,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Save package cache paths: - .buildcache/packages/store @@ -1128,7 +1203,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 65eca516163e4f0e2862988d2e4305d97e1a4cb4 + - PACKAGE_SPEC_ID: b971c9df855df00922afb57baeda53f399318c79 steps: - setup_remote_docker: docker_layer_caching: false @@ -1142,7 +1217,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Restore package cache - run: command: |2 @@ -1188,7 +1263,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Save package cache paths: - .buildcache/packages/store @@ -1200,7 +1275,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 761320ecae414d2a19b61e704d5f5883d5c6286e + - PACKAGE_SPEC_ID: a8981b00f8f6bce3fb156f73b58c4e33fe077876 steps: - setup_remote_docker: docker_layer_caching: false @@ -1214,7 +1289,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Restore package cache - run: command: |2 @@ -1260,7 +1335,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Save package cache paths: - .buildcache/packages/store @@ -1272,7 +1347,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 771602be638d83ed5ccdf6efab336e8ca5170cbf + - PACKAGE_SPEC_ID: 224e22a10a66af47d1f9468e0ca6c68f2a252599 steps: - setup_remote_docker: docker_layer_caching: false @@ -1286,7 +1361,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Restore package cache - run: command: |2 @@ -1332,7 +1407,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Save package cache paths: - .buildcache/packages/store @@ -1344,7 +1419,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: dfa2d7800b4d38d601ef4b977bc9256814b248bd + - PACKAGE_SPEC_ID: ccb0599d3f5653d5bf75173f87d906156ebf1e3d steps: - setup_remote_docker: docker_layer_caching: false @@ -1358,7 +1433,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Restore package cache - run: command: |2 @@ -1404,7 +1479,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Save package cache paths: - .buildcache/packages/store @@ -1416,7 +1491,7 @@ jobs: - AUTO_INSTALL_TOOLS: 'YES' - BUILDKIT_PROGRESS: plain - PRODUCT_REVISION: '' - - PACKAGE_SPEC_ID: 05943f4935af9c2c169f437d6b6d97f06e8d44fb + - PACKAGE_SPEC_ID: d92fb1003c4ac9e95b86d700d64bf54b76e6fef5 steps: - setup_remote_docker: docker_layer_caching: false @@ -1430,7 +1505,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Restore package cache - run: command: |2 @@ -1476,7 +1551,7 @@ jobs: command: ls -lahR .buildcache/packages name: List packages - save_cache: - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Save package cache paths: - .buildcache/packages/store @@ -1492,7 +1567,7 @@ jobs: make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=latest name: Run SQL PgTap Tests workflows: - build-d1ed8558d55be3da: + build-87386c0c55d5c61a: jobs: - build-common-layers - darwin_amd64_package: @@ -1501,6 +1576,7 @@ workflows: - bundle-releases: requires: - darwin_amd64_package + - darwin_arm64_package - freebsd_386_package - freebsd_amd64_package - freebsd_arm_package @@ -1515,6 +1591,9 @@ workflows: - solaris_amd64_package - windows_386_package - windows_amd64_package + - darwin_arm64_package: + requires: + - build-common-layers - freebsd_386_package: requires: - build-common-layers diff --git a/.circleci/config/@build-release.yml b/.circleci/config/@build-release.yml index d965ee894..8ec700004 100644 --- a/.circleci/config/@build-release.yml +++ b/.circleci/config/@build-release.yml @@ -136,49 +136,52 @@ jobs: command: mkdir -p .buildcache && echo "*" > .buildcache/.gitignore name: Ignore .buildcache - restore_cache: - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} name: Restore package cache - restore_cache: - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} name: Restore package cache - restore_cache: - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Restore package cache - restore_cache: - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Restore package cache - restore_cache: - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Restore package cache - restore_cache: - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Restore package cache - restore_cache: - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Restore package cache - restore_cache: - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Restore package cache - restore_cache: - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Restore package cache - restore_cache: - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Restore package cache - restore_cache: - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Restore package cache - restore_cache: - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Restore package cache - restore_cache: - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Restore package cache - restore_cache: - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Restore package cache - restore_cache: - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} + name: Restore package cache + - restore_cache: + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Restore package cache - run: command: make package-meta-all @@ -190,20 +193,20 @@ jobs: command: ls -lahR .buildcache name: List Build Cache - run: - command: cp packages*.lock/pkgs.yml lockfile-d1ed8558d55be3da.yml + command: cp packages*.lock/pkgs.yml lockfile-87386c0c55d5c61a.yml name: Update Lockfile Name - run: - command: tar -czf packages-d1ed8558d55be3da.tar.gz .buildcache/packages lockfile-d1ed8558d55be3da.yml + command: tar -czf packages-87386c0c55d5c61a.tar.gz .buildcache/packages lockfile-87386c0c55d5c61a.yml name: Create Raw Package Tarball - run: - command: tar -czf meta-d1ed8558d55be3da.tar.gz .buildcache/packages/store/*.json lockfile-d1ed8558d55be3da.yml + command: tar -czf meta-87386c0c55d5c61a.tar.gz .buildcache/packages/store/*.json lockfile-87386c0c55d5c61a.yml name: Create Metadata Tarball - store_artifacts: - path: lockfile-d1ed8558d55be3da.yml + path: lockfile-87386c0c55d5c61a.yml - store_artifacts: - path: packages-d1ed8558d55be3da.tar.gz + path: packages-87386c0c55d5c61a.tar.gz - store_artifacts: - path: meta-d1ed8558d55be3da.tar.gz + path: meta-87386c0c55d5c61a.tar.gz - store_artifacts: path: .buildcache/packages darwin_amd64_package: @@ -221,7 +224,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} name: Restore package cache - run: command: |2 @@ -269,10 +272,77 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} + key: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} name: Save package cache environment: - PACKAGE_SPEC_ID: a7184da6d6c92fdd0f1b0f68cb133f0b2325623f + PACKAGE_SPEC_ID: fa8840f2e78bfa9c58d242426d80f4e30464516f + darwin_arm64_package: + executor: builder + steps: + - setup_remote_docker: + version: 19.03.12 + docker_layer_caching: false + - add_ssh_keys: + fingerprints: + - 0e:03:77:f4:e2:c3:56:c2:53:6a:03:e1:31:91:2f:06 + - checkout: + path: . + - run: + command: make -C packages*.lock write-package-cache-key + name: Write package cache key + - restore_cache: + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} + name: Restore package cache + - run: + command: |2 + + if ! { PKG=$(find .buildcache/packages/store -maxdepth 1 -mindepth 1 -name '*.zip' 2> /dev/null) && [ -n "$PKG" ]; }; then + echo "No package found, continuing with build." + exit 0 + fi + echo "Package already cached, skipping build." + circleci-agent step halt + name: Check cache status + - run: + command: make -C packages*.lock write-builder-cache-keys + name: Write builder layer cache keys + - restore_cache: + key: copy-source_5c01_{{checksum ".buildcache/cache-keys/copy-source-5c01da64a31c9bfe7ba38f51d95e8bf99c0842f5"}} + keys: + - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} + - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} + - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} + name: 'Restore Builder Image Cache: copy-source' + - run: + command: make -C packages*.lock load-builder-cache + name: Load whatever builder cache we have (if any) into the Docker daemon + no_output_timeout: 30m + - run: + command: |2- + + KEYFILE="$HOME/.ssh/id_rsa_c6969882dc046c39ddac8305e3151c98" + eval "$(ssh-agent -s)" + if [ -f "$KEYFILE" ]; then + ssh-add "$KEYFILE" + else + echo "==> INFO: SSH key for github.com not found" + echo " Attempts to access private repositories from within" + echo " the build will fail, e.g. for private go modules, or" + echo " attempts to directly clone private repositories." + fi + + make -C packages*.lock package + name: Compile Package + - run: + command: ls -lahR .buildcache/packages + name: List packages + - save_cache: + paths: + - .buildcache/packages/store + key: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} + name: Save package cache + environment: + PACKAGE_SPEC_ID: 110a848ce5db07e7fc389430350c45261d2d7baa freebsd_386_package: executor: builder steps: @@ -288,7 +358,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Restore package cache - run: command: |2 @@ -336,10 +406,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} + key: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 51de04f17d8bd90f98ee7e90d29f7196114e9312 + PACKAGE_SPEC_ID: 36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d freebsd_amd64_package: executor: builder steps: @@ -355,7 +425,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Restore package cache - run: command: |2 @@ -403,10 +473,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} + key: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} name: Save package cache environment: - PACKAGE_SPEC_ID: a5b1302c80e9d70de2b213f330bf0ad0055c8b96 + PACKAGE_SPEC_ID: 0922d907d014ef141526a2a3b9d54a3940adcbf3 freebsd_arm_package: executor: builder steps: @@ -422,7 +492,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Restore package cache - run: command: |2 @@ -470,10 +540,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} + key: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} name: Save package cache environment: - PACKAGE_SPEC_ID: dfa2d7800b4d38d601ef4b977bc9256814b248bd + PACKAGE_SPEC_ID: ccb0599d3f5653d5bf75173f87d906156ebf1e3d linux_386_package: executor: builder steps: @@ -489,7 +559,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Restore package cache - run: command: |2 @@ -537,10 +607,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} + key: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 41b52d0b959e71aaac5d843b6329d0e762b29dd9 + PACKAGE_SPEC_ID: bced2a380b5354bba95ec9da4a54d06bc0a9b56d linux_amd64_package: executor: builder steps: @@ -556,7 +626,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Restore package cache - run: command: |2 @@ -604,10 +674,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} + key: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} name: Save package cache environment: - PACKAGE_SPEC_ID: a9f7f1be2569aac16eeb21967c3a47f4a04a118e + PACKAGE_SPEC_ID: 9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f linux_arm_package: executor: builder steps: @@ -623,7 +693,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Restore package cache - run: command: |2 @@ -671,10 +741,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} + key: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e + PACKAGE_SPEC_ID: 6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4 linux_arm64_package: executor: builder steps: @@ -690,7 +760,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Restore package cache - run: command: |2 @@ -738,10 +808,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} + key: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} name: Save package cache environment: - PACKAGE_SPEC_ID: c249aa18d9172af5dee394ffc56f6d5afc9b9470 + PACKAGE_SPEC_ID: 276415ef2d0ac06fb080dd55910bb71613f9c049 netbsd_386_package: executor: builder steps: @@ -757,7 +827,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Restore package cache - run: command: |2 @@ -805,10 +875,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} + key: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 771602be638d83ed5ccdf6efab336e8ca5170cbf + PACKAGE_SPEC_ID: 224e22a10a66af47d1f9468e0ca6c68f2a252599 netbsd_amd64_package: executor: builder steps: @@ -824,7 +894,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Restore package cache - run: command: |2 @@ -872,10 +942,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} + key: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 65eca516163e4f0e2862988d2e4305d97e1a4cb4 + PACKAGE_SPEC_ID: b971c9df855df00922afb57baeda53f399318c79 openbsd_386_package: executor: builder steps: @@ -891,7 +961,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Restore package cache - run: command: |2 @@ -939,10 +1009,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} + key: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 89b451e85a285500ed558e0fbc7f1838ae50c040 + PACKAGE_SPEC_ID: 1c8250fee830f8fac2364e7d3e3f82ee5832983e openbsd_amd64_package: executor: builder steps: @@ -958,7 +1028,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Restore package cache - run: command: |2 @@ -1006,10 +1076,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} + key: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 761320ecae414d2a19b61e704d5f5883d5c6286e + PACKAGE_SPEC_ID: a8981b00f8f6bce3fb156f73b58c4e33fe077876 solaris_amd64_package: executor: builder steps: @@ -1025,7 +1095,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Restore package cache - run: command: |2 @@ -1073,10 +1143,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} + key: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 505ce51e7cd78758af5b578825ba3395630bb1cf + PACKAGE_SPEC_ID: ee6ae4147a76888401e2032a2fee0926ef36ad00 windows_386_package: executor: builder steps: @@ -1092,7 +1162,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} name: Restore package cache - run: command: |2 @@ -1140,10 +1210,10 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} + key: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 4a9e88daa25103b845842c687fd8f6900e70f9a7 + PACKAGE_SPEC_ID: e3454f0b7ee22d489b83f6daf78d545f4878a79c windows_amd64_package: executor: builder steps: @@ -1159,7 +1229,7 @@ jobs: command: make -C packages*.lock write-package-cache-key name: Write package cache key - restore_cache: - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Restore package cache - run: command: |2 @@ -1207,12 +1277,12 @@ jobs: - save_cache: paths: - .buildcache/packages/store - key: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} + key: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} name: Save package cache environment: - PACKAGE_SPEC_ID: 05943f4935af9c2c169f437d6b6d97f06e8d44fb + PACKAGE_SPEC_ID: d92fb1003c4ac9e95b86d700d64bf54b76e6fef5 workflows: - build-d1ed8558d55be3da: + build-87386c0c55d5c61a: jobs: - build-common-layers: {} - darwin_amd64_package: @@ -1221,6 +1291,7 @@ workflows: - bundle-releases: requires: - darwin_amd64_package + - darwin_arm64_package - freebsd_386_package - freebsd_amd64_package - freebsd_arm_package @@ -1235,6 +1306,9 @@ workflows: - solaris_amd64_package - windows_386_package - windows_amd64_package + - darwin_arm64_package: + requires: + - build-common-layers - freebsd_386_package: requires: - build-common-layers diff --git a/.gitignore b/.gitignore index 23b0ea13e..5e180a751 100644 --- a/.gitignore +++ b/.gitignore @@ -120,6 +120,7 @@ main /bin/ update-ui-assets* /plugins/kms/assets/ +/plugins/host/assets/ # Test config file test*.hcl diff --git a/Makefile b/Makefile index 82b31935f..e228e39a7 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,6 @@ dev: BUILD_TAGS+=dev dev: BUILD_TAGS+=ui dev: build-ui-ifne @echo "==> Building Boundary with dev and UI features enabled" - @CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS)' BOUNDARY_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'" - -bin: BUILD_TAGS+=ui -bin: build-ui - @echo "==> Building Boundary with UI features enabled" @CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/build.sh'" fmt: @@ -171,6 +166,9 @@ test-database-down: make -C testing/dbtest/docker clean test-ci: install-go +test-ci: export CI_BUILD=1 +test-ci: + CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/build.sh'" ~/.go/bin/go test ./... -v $(TESTARGS) -timeout 120m test-sql: diff --git a/go.mod b/go.mod index f20552ee9..c5528515d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,13 @@ replace github.com/hashicorp/boundary/api => ./api replace github.com/hashicorp/boundary/sdk => ./sdk require ( + github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.21 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/armon/go-metrics v0.3.9 + github.com/aws/aws-sdk-go v1.40.44 // indirect github.com/bufbuild/buf v0.37.0 github.com/dhui/dktest v0.3.4 // indirect github.com/fatih/color v1.12.0 @@ -18,10 +24,8 @@ require ( github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e github.com/hashicorp/boundary/api v0.0.19 - github.com/hashicorp/boundary/sdk v0.0.12-0.20211005202728-158ab09952ba + github.com/hashicorp/boundary/sdk v0.0.12-0.20211007161358-65f3725bf3e1 github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 @@ -34,6 +38,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-rootcerts v1.0.2 + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 @@ -45,6 +50,7 @@ require ( github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 github.com/hashicorp/go-uuid v1.0.2 + github.com/hashicorp/go-version v1.3.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/vault/api v1.1.1 github.com/hashicorp/vault/sdk v0.2.1 @@ -77,6 +83,7 @@ require ( golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 golang.org/x/term v0.0.0-20210916214954-140adaaadfaf golang.org/x/tools v0.1.6 + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 google.golang.org/grpc v1.40.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 diff --git a/go.sum b/go.sum index 7ded69351..6ee21fb45 100644 --- a/go.sum +++ b/go.sum @@ -134,7 +134,6 @@ github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= @@ -378,7 +377,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -461,10 +459,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578 h1:XrH7otQgHtqcYX1qbO10DypzjEBwYdtYcIEc9d1BG5w= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210928175609-6345faccf578/go.mod h1:TbqGUjL8yDVqdoEHCiEPoE7lv5yfQRKxqPinfGUeC30= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e h1:UfEvqkWfyIQ846nFeAWKnS9HAtP9xLk3NxqU3+zLeZY= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20210930043837-3542fae64d2e/go.mod h1:dtbUh5NNMbV9pZNncSNo3JPoG3zm9LeMBYz+lKVdv0c= github.com/hashicorp/cap v0.1.1 h1:GjO4+9+H0wv/89YoEsxeVc2jIizL19r5v5l2lpaH8Kg= github.com/hashicorp/cap v0.1.1/go.mod h1:VfBvK2ULRyqsuqAnjgZl7HJ7/CGMC7ro4H5eXiZuun8= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -510,6 +504,7 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= @@ -571,8 +566,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= -github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= @@ -581,6 +574,7 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:W github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= @@ -732,7 +726,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -748,8 +741,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/manicminer/hamilton v0.30.0 h1:n7og9/Xh4d/JX0XG0uG1rv2vlkIkz8jwynCWoQszZvA= -github.com/manicminer/hamilton v0.30.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -788,6 +779,7 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -836,6 +828,7 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -953,7 +946,6 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= @@ -1036,7 +1028,6 @@ github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= @@ -1058,8 +1049,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE= github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8= -github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= -github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1166,7 +1155,6 @@ golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1216,7 +1204,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1228,7 +1215,6 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/internal/cmd/base/dev.go b/internal/cmd/base/dev.go index 1cc1f94fd..375616ff3 100644 --- a/internal/cmd/base/dev.go +++ b/internal/cmd/base/dev.go @@ -150,7 +150,7 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error { hostplugin.WithDescription("Provides an initial loopback host plugin in Boundary"), hostplugin.WithPublicId(b.DevLoopbackHostPluginId), } - if _, err = b.CreateHostPlugin(ctx, "loopback", plg, opts...); err != nil { + if _, err = b.RegisterHostPlugin(ctx, "loopback", plg, opts...); err != nil { return err } } diff --git a/internal/cmd/base/initial_resources.go b/internal/cmd/base/initial_resources.go index d9c7fb211..f57319ae0 100644 --- a/internal/cmd/base/initial_resources.go +++ b/internal/cmd/base/initial_resources.go @@ -587,11 +587,11 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) return tt, nil } -// CreateHostPlugin creates a host plugin in the database if not present. +// RegisterHostPlugin creates a host plugin in the database if not present. // It also registers the plugin in the shared map of running plugins. Since // all boundary provided host plugins must have a name, a name is required -// when calling CreateHostPlugin and will be used even if WithName is provided. -func (b *Server) CreateHostPlugin(ctx context.Context, name string, plg plgpb.HostPluginServiceClient, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { +// when calling RegisterHostPlugin and will be used even if WithName is provided. +func (b *Server) RegisterHostPlugin(ctx context.Context, name string, plg plgpb.HostPluginServiceClient, opt ...hostplugin.Option) (*hostplugin.Plugin, error) { if name == "" { return nil, fmt.Errorf("no name provided when creating plugin.") } diff --git a/internal/cmd/commands/database/migrate.go b/internal/cmd/commands/database/migrate.go index 514c453c3..9cfc38753 100644 --- a/internal/cmd/commands/database/migrate.go +++ b/internal/cmd/commands/database/migrate.go @@ -6,7 +6,10 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/errors" + host_plugin_assets "github.com/hashicorp/boundary/plugins/host" + external_host_plugins "github.com/hashicorp/boundary/sdk/plugins/host" "github.com/hashicorp/boundary/sdk/wrapper" + "github.com/hashicorp/go-hclog" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/parseutil" @@ -128,6 +131,43 @@ func (c *MigrateCommand) Run(args []string) (retCode int) { }() } + _, awsCleanup, err := external_host_plugins.CreateHostPlugin( + c.Context, + "aws", + external_host_plugins.WithHostPluginsFilesystem("boundary-plugin-host-", host_plugin_assets.FileSystem()), + external_host_plugins.WithHostPluginExecutionDir(c.Config.Plugins.ExecutionDir), + external_host_plugins.WithLogger(hclog.NewNullLogger())) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating dynamic host plugin: %w", err).Error()) + c.UI.Warn(base.WrapAtLength( + "Starting in Boundary 0.7.0, plugins are being introduced for " + + "various parts of the system. These plugins are created by writing " + + "out and executing plugin binaries. The migration function performed " + + "a check to ensure this system is capable of running plugins and " + + "encountered an error. The ability to run plugins will eventually " + + "become mandatory (for instance, for KMS implementations), so we are " + + "ensuring that it's feasible on a given system before migrating the " + + "database to the new version of Boundary that requires this capability. " + + "If your temporary directory is not writable and/or you cannot execute " + + "binaries in that directory, try setting the field " + + `"execution_dir" in the "plugins" block in the configuration file:`)) + c.UI.Warn(` +plugins { + execution_dir = +} +`) + c.UI.Warn(base.WrapAtLength( + "Otherwise, please file a bug at " + + "https://github.com/hashicorp/boundary/issues/new/choose and tell us " + + "what the error message is, along with details about your environment. " + + "We are committed to resolving any issues as quickly as possible.")) + return base.CommandCliError + } + if err := awsCleanup(); err != nil { + c.UI.Error(fmt.Errorf("Error running plugin cleanup function: %w", err).Error()) + return base.CommandCliError + } + dialect := "postgres" c.srv = base.NewServer(&base.Command{UI: c.UI}) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 5bdc01e70..71e0f8e4b 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -79,6 +79,7 @@ type Command struct { flagEveryEventAllowFilters []string flagEveryEventDenyFilters []string flagCreateLoopbackHostCatalogPlugin bool + flagPluginExecutionDir string } func (c *Command) Synopsis() string { @@ -310,6 +311,13 @@ func (c *Command) Flags() *base.FlagSets { Usage: `The optional every event deny filter. May be specified multiple times.`, }) + f.StringVar(&base.StringVar{ + Name: "plugin-execution-dir", + Target: &c.flagPluginExecutionDir, + EnvVar: "BOUNDARY_DEV_PLUGIN_EXECUTION_DIR", + Usage: "Specifies where Boundary should write plugins that it is executing; if not set defaults to system temp directory.", + }) + f.BoolVar(&base.BoolVar{ Name: "create-loopback-hostcatalog-plugin", Target: &c.flagCreateLoopbackHostCatalogPlugin, @@ -351,6 +359,7 @@ func (c *Command) Run(args []string) int { c.UI.Error(fmt.Errorf("Error creating controller dev config: %w", err).Error()) return base.CommandUserError } + if c.flagWorkerAuthKey != "" { c.Config.DevWorkerAuthKey = c.flagWorkerAuthKey for _, kms := range c.Config.Seals { @@ -359,6 +368,13 @@ func (c *Command) Run(args []string) int { } } } + + c.DevLoginName = c.flagLoginName + c.DevPassword = c.flagPassword + c.DevUnprivilegedLoginName = c.flagUnprivilegedLoginName + c.DevUnprivilegedPassword = c.flagUnprivilegedPassword + c.DevTargetDefaultPort = c.flagTargetDefaultPort + c.Config.Plugins.ExecutionDir = c.flagPluginExecutionDir if c.flagIdSuffix != "" { if len(c.flagIdSuffix) != 10 { c.UI.Error("Invalid ID suffix, must be exactly 10 characters") @@ -383,19 +399,7 @@ func (c *Command) Run(args []string) int { c.DevHostId = fmt.Sprintf("%s_%s", static.HostPrefix, c.flagIdSuffix) c.DevTargetId = fmt.Sprintf("%s_%s", target.TcpTargetPrefix, c.flagIdSuffix) } - if c.flagLoginName != "" { - c.DevLoginName = c.flagLoginName - } - if c.flagPassword != "" { - c.DevPassword = c.flagPassword - } - if c.flagUnprivilegedLoginName != "" { - c.DevUnprivilegedLoginName = c.flagUnprivilegedLoginName - } - if c.flagUnprivilegedPassword != "" { - c.DevUnprivilegedPassword = c.flagUnprivilegedPassword - } - c.DevTargetDefaultPort = c.flagTargetDefaultPort + host, port, err := net.SplitHostPort(c.flagHostAddress) if err != nil { if !strings.Contains(err.Error(), "missing port") { diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 83e4f382c..bfc144525 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -105,6 +105,9 @@ type Config struct { // Eventing configuration for the controller Eventing *event.EventerConfig `hcl:"events"` + + // Plugin-related options + Plugins Plugins `hcl:"plugins"` } type Controller struct { @@ -186,6 +189,10 @@ type Database struct { MaxOpenConnections int `hcl:"max_open_connections"` } +type Plugins struct { + ExecutionDir string `hcl:"execution_dir"` +} + // DevWorker is a Config that is used for dev mode of Boundary // workers func DevWorker() (*Config, error) { diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 7e73a4cdd..3f93c08c9 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -140,6 +140,19 @@ func TestDevController(t *testing.T) { assert.Empty(t, l0.CorsAllowedOrigins) assert.True(t, *l0.CorsDisableDefaultAllowedOriginValues) } + + // Test plugins block + { + // CORS disabled + conf := ` + plugins { + execution_dir = "/tmp/foobar" + } + ` + actual, err = Parse(conf) + assert.NoError(t, err) + assert.Equal(t, actual.Plugins.ExecutionDir, "/tmp/foobar") + } } func TestDevWorker(t *testing.T) { diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 4c589f874..9aa83c540 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -6,8 +6,6 @@ import ( "fmt" "sync" - awsplg "github.com/hashicorp/boundary-plugin-host-aws/plugin" - azureplg "github.com/hashicorp/boundary-plugin-host-azure/plugin" "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" @@ -27,7 +25,9 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + host_plugin_assets "github.com/hashicorp/boundary/plugins/host" "github.com/hashicorp/boundary/sdk/pbs/plugin" + external_host_plugins "github.com/hashicorp/boundary/sdk/plugins/host" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/patrickmn/go-cache" @@ -107,12 +107,34 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } } - if _, err := conf.CreateHostPlugin(ctx, "azure", pluginhost.NewWrappingPluginClient(&azureplg.AzurePlugin{}), hostplugin.WithDescription("Boundary provided host plugin for Azure.")); err != nil { - return nil, fmt.Errorf("error creating azure host plugin: %w", err) + azureSvcClient, azureCleanup, err := external_host_plugins.CreateHostPlugin( + ctx, + "azure", + external_host_plugins.WithHostPluginsFilesystem("boundary-plugin-host-", host_plugin_assets.FileSystem()), + external_host_plugins.WithHostPluginExecutionDir(conf.RawConfig.Plugins.ExecutionDir), + external_host_plugins.WithLogger(hclog.NewNullLogger())) + if err != nil { + return nil, fmt.Errorf("error creating azure host plugin") } - if _, err := conf.CreateHostPlugin(ctx, "aws", pluginhost.NewWrappingPluginClient(&awsplg.AwsPlugin{}), hostplugin.WithDescription("Boundary provided host plugin for AWS.")); err != nil { - return nil, fmt.Errorf("error creating aws host plugin: %w", err) + conf.ShutdownFuncs = append(conf.ShutdownFuncs, azureCleanup) + if _, err := conf.RegisterHostPlugin(ctx, "azure", azureSvcClient, hostplugin.WithDescription("Built-in Azure host plugin")); err != nil { + return nil, fmt.Errorf("error registering azure host plugin: %w", err) } + + awsSvcClient, awsCleanup, err := external_host_plugins.CreateHostPlugin( + ctx, + "aws", + external_host_plugins.WithHostPluginsFilesystem("boundary-plugin-host-", host_plugin_assets.FileSystem()), + external_host_plugins.WithHostPluginExecutionDir(conf.RawConfig.Plugins.ExecutionDir), + external_host_plugins.WithLogger(hclog.NewNullLogger())) + if err != nil { + return nil, fmt.Errorf("error creating aws host plugin") + } + conf.ShutdownFuncs = append(conf.ShutdownFuncs, awsCleanup) + if _, err := conf.RegisterHostPlugin(ctx, "aws", awsSvcClient, hostplugin.WithDescription("Built-in AWS host plugin")); err != nil { + return nil, fmt.Errorf("error registering aws host plugin: %w", err) + } + if conf.HostPlugins == nil { conf.HostPlugins = make(map[string]plugin.HostPluginServiceClient) } diff --git a/internal/servers/controller/handlers/subtypes_test.go b/internal/servers/controller/handlers/subtypes_test.go index ae4e1263b..6c302642c 100644 --- a/internal/servers/controller/handlers/subtypes_test.go +++ b/internal/servers/controller/handlers/subtypes_test.go @@ -54,7 +54,7 @@ func TestStructToProtoToStruct(t *testing.T) { assert.Empty(t, cmp.Diff(wantStruct, st, protocmp.Transform())) newAttr := tc.pb.ProtoReflect().New().Interface() - StructToProto(st, newAttr) + require.NoError(t, StructToProto(st, newAttr)) assert.Equal(t, tc.pb, newAttr) }) } diff --git a/packages-oss.lock/pkgs.yml b/packages-oss.lock/pkgs.yml index 4007e2d6f..dab9ecbfe 100644 --- a/packages-oss.lock/pkgs.yml +++ b/packages-oss.lock/pkgs.yml @@ -1,11 +1,11 @@ # *** # WARNING: Do not EDIT or MERGE this file, it is generated by packagespec. # *** -lockid: d1ed8558d55be3da +lockid: 87386c0c55d5c61a packagespec-version: 0.2.6 cache-version: 10 packages: - - packagespecid: a7184da6d6c92fdd0f1b0f68cb133f0b2325623f + - packagespecid: fa8840f2e78bfa9c58d242426d80f4e30464516f inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -33,7 +33,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f POST_PROCESSORS: apple-notarize av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -41,14 +41,57 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f-{{checksum ".buildcache/cache-keys/package-a7184da6d6c92fdd0f1b0f68cb133f0b2325623f"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-fa8840f2e78bfa9c58d242426d80f4e30464516f-{{checksum ".buildcache/cache-keys/package-fa8840f2e78bfa9c58d242426d80f4e30464516f"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_darwin_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_darwin_amd64.zip - - packagespecid: 51de04f17d8bd90f98ee7e90d29f7196114e9312 + - packagespecid: 110a848ce5db07e7fc389430350c45261d2d7baa + inputs: + BINARY_NAME: boundary + BUILD_TAGS: ui + CGO_ENABLED: "0" + GOARCH: arm64 + GOOS: darwin + PRODUCT_NAME: boundary + PRODUCT_VERSION: 0.6.2 + PRODUCT_VERSION_MMP: 0.6.2 + PRODUCT_VERSION_PRE: "" + meta: + BUILD_JOB_NAME: darwin_arm64_package + BUNDLE_NAME: boundary_0.6.2 + builtin: + BUILD_LAYERS: + - type: copy-source + name: 03-copy-source-5c01da64a31c9bfe7ba38f51d95e8bf99c0842f5 + archive: .buildcache/archives/03-copy-source-5c01da64a31c9bfe7ba38f51d95e8bf99c0842f5.tar.gz + - type: go-modules + name: 02-go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be + archive: .buildcache/archives/02-go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be.tar.gz + - type: ui + name: 01-ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05 + archive: .buildcache/archives/01-ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05.tar.gz + - type: base + name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 + archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa + POST_PROCESSORS: apple-notarize av-scan + circleci: + BUILDER_CACHE_KEY_PREFIX_LIST: + - copy-source_5c01_{{checksum ".buildcache/cache-keys/copy-source-5c01da64a31c9bfe7ba38f51d95e8bf99c0842f5"}} + - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} + - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} + - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} + PACKAGE_CACHE_KEY: package-110a848ce5db07e7fc389430350c45261d2d7baa-{{checksum ".buildcache/cache-keys/package-110a848ce5db07e7fc389430350c45261d2d7baa"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + aliases: + - type: local + path: boundary_0.6.2_darwin_arm64.zip + - type: public-hc-releases + path: boundary/boundary_0.6.2/boundary_0.6.2_darwin_arm64.zip + - packagespecid: 36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -76,7 +119,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -84,14 +127,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-51de04f17d8bd90f98ee7e90d29f7196114e9312-{{checksum ".buildcache/cache-keys/package-51de04f17d8bd90f98ee7e90d29f7196114e9312"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d-{{checksum ".buildcache/cache-keys/package-36032b26f94d618cdc87dcb7ac3b4eb820a4ab6d"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_freebsd_386.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_freebsd_386.zip - - packagespecid: a5b1302c80e9d70de2b213f330bf0ad0055c8b96 + - packagespecid: 0922d907d014ef141526a2a3b9d54a3940adcbf3 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -119,7 +162,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -127,14 +170,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96-{{checksum ".buildcache/cache-keys/package-a5b1302c80e9d70de2b213f330bf0ad0055c8b96"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-0922d907d014ef141526a2a3b9d54a3940adcbf3-{{checksum ".buildcache/cache-keys/package-0922d907d014ef141526a2a3b9d54a3940adcbf3"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_freebsd_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_freebsd_amd64.zip - - packagespecid: dfa2d7800b4d38d601ef4b977bc9256814b248bd + - packagespecid: ccb0599d3f5653d5bf75173f87d906156ebf1e3d inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -162,7 +205,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -170,14 +213,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-dfa2d7800b4d38d601ef4b977bc9256814b248bd-{{checksum ".buildcache/cache-keys/package-dfa2d7800b4d38d601ef4b977bc9256814b248bd"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d-{{checksum ".buildcache/cache-keys/package-ccb0599d3f5653d5bf75173f87d906156ebf1e3d"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_freebsd_arm.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_freebsd_arm.zip - - packagespecid: 41b52d0b959e71aaac5d843b6329d0e762b29dd9 + - packagespecid: bced2a380b5354bba95ec9da4a54d06bc0a9b56d inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -205,7 +248,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -213,14 +256,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-41b52d0b959e71aaac5d843b6329d0e762b29dd9-{{checksum ".buildcache/cache-keys/package-41b52d0b959e71aaac5d843b6329d0e762b29dd9"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d-{{checksum ".buildcache/cache-keys/package-bced2a380b5354bba95ec9da4a54d06bc0a9b56d"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_linux_386.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_linux_386.zip - - packagespecid: a9f7f1be2569aac16eeb21967c3a47f4a04a118e + - packagespecid: 9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -248,7 +291,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -256,14 +299,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e-{{checksum ".buildcache/cache-keys/package-a9f7f1be2569aac16eeb21967c3a47f4a04a118e"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f-{{checksum ".buildcache/cache-keys/package-9cdfa7cc4f940bdb711b3e4c05e23a771d7ce29f"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_linux_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_linux_amd64.zip - - packagespecid: 63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e + - packagespecid: 6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -291,7 +334,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -299,14 +342,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e-{{checksum ".buildcache/cache-keys/package-63d6f7ef20e846b9c2eac3c7a63d2b48b4f0499e"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4-{{checksum ".buildcache/cache-keys/package-6c8214c1a10a7f53964bfb8d2688cadbe8c6b9e4"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_linux_arm.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_linux_arm.zip - - packagespecid: c249aa18d9172af5dee394ffc56f6d5afc9b9470 + - packagespecid: 276415ef2d0ac06fb080dd55910bb71613f9c049 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -334,7 +377,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -342,14 +385,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-c249aa18d9172af5dee394ffc56f6d5afc9b9470-{{checksum ".buildcache/cache-keys/package-c249aa18d9172af5dee394ffc56f6d5afc9b9470"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-276415ef2d0ac06fb080dd55910bb71613f9c049-{{checksum ".buildcache/cache-keys/package-276415ef2d0ac06fb080dd55910bb71613f9c049"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_linux_arm64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_linux_arm64.zip - - packagespecid: 771602be638d83ed5ccdf6efab336e8ca5170cbf + - packagespecid: 224e22a10a66af47d1f9468e0ca6c68f2a252599 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -377,7 +420,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -385,14 +428,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-771602be638d83ed5ccdf6efab336e8ca5170cbf-{{checksum ".buildcache/cache-keys/package-771602be638d83ed5ccdf6efab336e8ca5170cbf"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-224e22a10a66af47d1f9468e0ca6c68f2a252599-{{checksum ".buildcache/cache-keys/package-224e22a10a66af47d1f9468e0ca6c68f2a252599"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_netbsd_386.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_netbsd_386.zip - - packagespecid: 65eca516163e4f0e2862988d2e4305d97e1a4cb4 + - packagespecid: b971c9df855df00922afb57baeda53f399318c79 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -420,7 +463,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -428,14 +471,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-65eca516163e4f0e2862988d2e4305d97e1a4cb4-{{checksum ".buildcache/cache-keys/package-65eca516163e4f0e2862988d2e4305d97e1a4cb4"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-b971c9df855df00922afb57baeda53f399318c79-{{checksum ".buildcache/cache-keys/package-b971c9df855df00922afb57baeda53f399318c79"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_netbsd_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_netbsd_amd64.zip - - packagespecid: 89b451e85a285500ed558e0fbc7f1838ae50c040 + - packagespecid: 1c8250fee830f8fac2364e7d3e3f82ee5832983e inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -463,7 +506,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -471,14 +514,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-89b451e85a285500ed558e0fbc7f1838ae50c040-{{checksum ".buildcache/cache-keys/package-89b451e85a285500ed558e0fbc7f1838ae50c040"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-1c8250fee830f8fac2364e7d3e3f82ee5832983e-{{checksum ".buildcache/cache-keys/package-1c8250fee830f8fac2364e7d3e3f82ee5832983e"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_openbsd_386.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_openbsd_386.zip - - packagespecid: 761320ecae414d2a19b61e704d5f5883d5c6286e + - packagespecid: a8981b00f8f6bce3fb156f73b58c4e33fe077876 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -506,7 +549,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -514,14 +557,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-761320ecae414d2a19b61e704d5f5883d5c6286e-{{checksum ".buildcache/cache-keys/package-761320ecae414d2a19b61e704d5f5883d5c6286e"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-a8981b00f8f6bce3fb156f73b58c4e33fe077876-{{checksum ".buildcache/cache-keys/package-a8981b00f8f6bce3fb156f73b58c4e33fe077876"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_openbsd_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_openbsd_amd64.zip - - packagespecid: 505ce51e7cd78758af5b578825ba3395630bb1cf + - packagespecid: ee6ae4147a76888401e2032a2fee0926ef36ad00 inputs: BINARY_NAME: boundary BUILD_TAGS: ui @@ -549,7 +592,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00 POST_PROCESSORS: av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -557,14 +600,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-505ce51e7cd78758af5b578825ba3395630bb1cf-{{checksum ".buildcache/cache-keys/package-505ce51e7cd78758af5b578825ba3395630bb1cf"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary + PACKAGE_CACHE_KEY: package-ee6ae4147a76888401e2032a2fee0926ef36ad00-{{checksum ".buildcache/cache-keys/package-ee6ae4147a76888401e2032a2fee0926ef36ad00"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary aliases: - type: local path: boundary_0.6.2_solaris_amd64.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_solaris_amd64.zip - - packagespecid: 4a9e88daa25103b845842c687fd8f6900e70f9a7 + - packagespecid: e3454f0b7ee22d489b83f6daf78d545f4878a79c inputs: BINARY_NAME: boundary.exe BUILD_TAGS: ui @@ -592,7 +635,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7 + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c POST_PROCESSORS: microsoft-notarize av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -600,14 +643,14 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-4a9e88daa25103b845842c687fd8f6900e70f9a7-{{checksum ".buildcache/cache-keys/package-4a9e88daa25103b845842c687fd8f6900e70f9a7"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary.exe ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary.exe + PACKAGE_CACHE_KEY: package-e3454f0b7ee22d489b83f6daf78d545f4878a79c-{{checksum ".buildcache/cache-keys/package-e3454f0b7ee22d489b83f6daf78d545f4878a79c"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary.exe ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary.exe aliases: - type: local path: boundary_0.6.2_windows_386.zip - type: public-hc-releases path: boundary/boundary_0.6.2/boundary_0.6.2_windows_386.zip - - packagespecid: 05943f4935af9c2c169f437d6b6d97f06e8d44fb + - packagespecid: d92fb1003c4ac9e95b86d700d64bf54b76e6fef5 inputs: BINARY_NAME: boundary.exe BUILD_TAGS: ui @@ -635,7 +678,7 @@ packages: - type: base name: 00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9 archive: .buildcache/archives/00-base-849b90f357ec2b0e340c99c0a49e883d36073bd9.tar.gz - PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb + PACKAGE_CACHE_KEY_FILE: .buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5 POST_PROCESSORS: microsoft-notarize av-scan circleci: BUILDER_CACHE_KEY_PREFIX_LIST: @@ -643,8 +686,8 @@ packages: - go-modules_a25c_{{checksum ".buildcache/cache-keys/go-modules-a25cdf16b83859e7e53c8fd5272e4c6b8366e2be"}} - ui_7d0e_{{checksum ".buildcache/cache-keys/ui-7d0eb4b8e23fd3d22cf70da1dff7e4359673ce05"}} - base_849b_{{checksum ".buildcache/cache-keys/base-849b90f357ec2b0e340c99c0a49e883d36073bd9"}} - PACKAGE_CACHE_KEY: package-05943f4935af9c2c169f437d6b6d97f06e8d44fb-{{checksum ".buildcache/cache-keys/package-05943f4935af9c2c169f437d6b6d97f06e8d44fb"}} - build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary.exe ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary.exe + PACKAGE_CACHE_KEY: package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5-{{checksum ".buildcache/cache-keys/package-d92fb1003c4ac9e95b86d700d64bf54b76e6fef5"}} + build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; ORIG_PATH=$(pwd); for PLUGIN_TYPE in host; do for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; cd $ORIG_PATH; done; cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; for CURR_PLUGIN in $(ls); do gzip -9 $CURR_PLUGIN; done; cd $ORIG_PATH; done; go build -v -tags 'ui' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version=0.6.2 -X $VERSION_PKG_PATH.VersionPrerelease=" -o $OUTPUT_DIR/boundary.exe ./cmd/boundary && cd $OUTPUT_DIR && zip $PACKAGE_ZIP_NAME boundary.exe aliases: - type: local path: boundary_0.6.2_windows_amd64.zip diff --git a/packages-oss.yml b/packages-oss.yml index eafa65656..b8789f581 100644 --- a/packages-oss.yml +++ b/packages-oss.yml @@ -21,6 +21,7 @@ inputs: packages: - inputs: { GOOS: darwin, GOARCH: amd64 } + - inputs: { GOOS: darwin, GOARCH: arm64 } - inputs: { GOOS: freebsd, GOARCH: 386 } - inputs: { GOOS: freebsd, GOARCH: amd64 } - inputs: { GOOS: freebsd, GOARCH: arm } @@ -96,6 +97,19 @@ layers: build-command: VERSION_PKG_PATH=github.com/hashicorp/boundary/version; unset GOPATH; + ORIG_PATH=$(pwd); + for PLUGIN_TYPE in host; do + for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do + cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; + go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN} .; + cd $ORIG_PATH; + done; + cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; + for CURR_PLUGIN in $(ls); do + gzip -9 $CURR_PLUGIN; + done; + cd $ORIG_PATH; + done; go build -v -tags '{{.BUILD_TAGS}}' -ldflags "-X $VERSION_PKG_PATH.GitCommit=$PACKAGE_SOURCE_ID -X $VERSION_PKG_PATH.Version={{.PRODUCT_VERSION_MMP}} diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 000000000..64f20d00f --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,6 @@ +# Boundary External Plugins Modules + +The Go modules under `plugins` are not intended to be used directly. They are +here purely to allow compilation of the various plugins Boundary supports +without pulling the dependencies (and any clashes, and any init behavior) +directly into Boundary. diff --git a/plugins/host/assets.go b/plugins/host/assets.go new file mode 100644 index 000000000..4790c5733 --- /dev/null +++ b/plugins/host/assets.go @@ -0,0 +1,21 @@ +package host_plugin_assets + +import ( + "embed" + "io/fs" +) + +const contentDir = "assets" + +// content is our static web server content. +//go:embed assets +var content embed.FS + +func FileSystem() fs.FS { + // Remove the root + f, err := fs.Sub(content, contentDir) + if err != nil { + panic(err) + } + return f +} diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod new file mode 100644 index 000000000..6ee93a4cf --- /dev/null +++ b/plugins/host/mains/aws/go.mod @@ -0,0 +1,17 @@ +module github.com/hashicorp/boundary/plugins/host/mains/aws + +go 1.16 + +require ( + github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.21 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e + github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 + github.com/hashicorp/go-hclog v0.16.2 + github.com/hashicorp/go-plugin v1.4.3 // indirect + golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect + google.golang.org/appengine v1.6.7 // indirect +) diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum new file mode 100644 index 000000000..3db4f374e --- /dev/null +++ b/plugins/host/mains/aws/go.sum @@ -0,0 +1,891 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v57.4.0+incompatible h1:uzhy/mTwu4rq6Be/fJJnE82PRx2A2vjGjzsA3aQ6Qu0= +github.com/Azure/azure-sdk-for-go v57.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= +github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= +github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= +github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= +github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f h1:oRD16bhpKNAanfcDDVU+J0NXqsgHIvGbbe/sy+r6Rs0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= +github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.40.44 h1:kECaYybTWYZY5IKHvQMxbE6Wi5Qrb+7hbkV7zQV3Sg8= +github.com/aws/aws-sdk-go v1.40.44/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e h1:7MimW+cEw3Hcr4NMQe3M8jJHYW40Y9oTSR6zyHzs31E= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e/go.mod h1:TbqGUjL8yDVqdoEHCiEPoE7lv5yfQRKxqPinfGUeC30= +github.com/hashicorp/boundary/sdk v0.0.11-0.20210926154843-a834a147850b/go.mod h1:ZkKPk/KyB4sMF9awLNQMBBS1I+8E/K3Ap5uPe36sGhM= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 h1:LYB55XVeUZUJXH/UU51BY+s7/z4BlLlc9NemlTFFlrE= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= +github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 h1:RKYUGplnpDoE/Cj2vupQqUJS/wWBVvmpxEYeEpGL88s= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping v0.6.4/go.mod h1:pRK4UPSmNLEAjLE2qVz7nvS4NTZyyypQCn2G4q7vkps= +github.com/hashicorp/go-kms-wrapping v0.6.5/go.mod h1:9aLqFi2fqK1WMCyTe9bVjB0X6XxCxD+oINL1ovxpgJE= +github.com/hashicorp/go-kms-wrapping v0.6.6 h1:S2lw/8CFAw5kcztPD9iABG8ekjOs5san9C2oZrY6Syw= +github.com/hashicorp/go-kms-wrapping v0.6.6/go.mod h1:9aLqFi2fqK1WMCyTe9bVjB0X6XxCxD+oINL1ovxpgJE= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 h1:aCPwC2WPyomaw5gk5ZZ7DPevN8d9DS3igkmq6DxGMjU= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7mduW2Q+2FMrXawkLM8e7NU= +github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ= +github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1 h1:HlTofNQN49DTzPCMVDppp+AVxMNVWaHUElpUYC0TRRE= +github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1/go.mod h1:irE9ILwhE8VooWdhHhBy0qXzOMRlHKoWoMlr31mAFCE= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI= +github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= +github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= +github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= +github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= +github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= +github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= +github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= +github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huaweicloud/golangsdk v0.0.0-20200304081349-45ec0797f2a4/go.mod h1:WQBcHRNX9shz3928lWEvstQJtAtYI7ks6XlgtRT9Tcw= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4= +github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/oracle/oci-go-sdk v12.5.0+incompatible h1:pr08ECoaDKHWO9tnzJB1YqClEs7ZK1CFOez2DQocH14= +github.com/oracle/oci-go-sdk v12.5.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= +github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/yandex-cloud/go-genproto v0.0.0-20200722140432-762fe965ce77/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= +github.com/yandex-cloud/go-sdk v0.0.0-20200722140627-2194e5077f13/go.mod h1:LEdAMqa1v/7KYe4b13ALLkonuDxLph57ibUb50ctvJk= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok= +go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= +go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/plugins/host/mains/aws/main.go b/plugins/host/mains/aws/main.go new file mode 100644 index 000000000..5b488e769 --- /dev/null +++ b/plugins/host/mains/aws/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "os" + + awshp "github.com/hashicorp/boundary-plugin-host-aws/plugin" + hp "github.com/hashicorp/boundary/sdk/plugins/host" + "github.com/hashicorp/go-hclog" +) + +func main() { + if err := hp.ServeHostPlugin(new(awshp.AwsPlugin), hp.WithLogger(hclog.NewNullLogger())); err != nil { + fmt.Println("Error serving plugin", err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/plugins/host/mains/azure/go.mod b/plugins/host/mains/azure/go.mod new file mode 100644 index 000000000..482fe9d58 --- /dev/null +++ b/plugins/host/mains/azure/go.mod @@ -0,0 +1,10 @@ +module github.com/hashicorp/boundary/plugins/host/mains/azure + +go 1.16 + +require ( + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f + github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 + github.com/hashicorp/go-hclog v0.16.2 + github.com/hashicorp/go-plugin v1.4.3 // indirect +) diff --git a/plugins/host/mains/azure/go.sum b/plugins/host/mains/azure/go.sum new file mode 100644 index 000000000..245f88f14 --- /dev/null +++ b/plugins/host/mains/azure/go.sum @@ -0,0 +1,874 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM= +github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= +github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= +github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= +github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= +github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f h1:oRD16bhpKNAanfcDDVU+J0NXqsgHIvGbbe/sy+r6Rs0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= +github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= +github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f h1:F0xrvXzIxKlRPjbrGjHGvqMuC4+txANqQBtnbjniVBc= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f/go.mod h1:0oG9826pkbPn9Aq0PSyKTn2WVxCP4ruaMUulVTplSI8= +github.com/hashicorp/boundary/sdk v0.0.11-0.20210928201932-8b1ca4b0b8b9/go.mod h1:ZkKPk/KyB4sMF9awLNQMBBS1I+8E/K3Ap5uPe36sGhM= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 h1:LYB55XVeUZUJXH/UU51BY+s7/z4BlLlc9NemlTFFlrE= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= +github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 h1:RKYUGplnpDoE/Cj2vupQqUJS/wWBVvmpxEYeEpGL88s= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping v0.6.4/go.mod h1:pRK4UPSmNLEAjLE2qVz7nvS4NTZyyypQCn2G4q7vkps= +github.com/hashicorp/go-kms-wrapping v0.6.5/go.mod h1:9aLqFi2fqK1WMCyTe9bVjB0X6XxCxD+oINL1ovxpgJE= +github.com/hashicorp/go-kms-wrapping v0.6.6 h1:S2lw/8CFAw5kcztPD9iABG8ekjOs5san9C2oZrY6Syw= +github.com/hashicorp/go-kms-wrapping v0.6.6/go.mod h1:9aLqFi2fqK1WMCyTe9bVjB0X6XxCxD+oINL1ovxpgJE= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2 h1:AEcLbDoaRC9JMmtZXsuCykztH53rvHsQFnwhoKtpNQM= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7mduW2Q+2FMrXawkLM8e7NU= +github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ= +github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1 h1:HlTofNQN49DTzPCMVDppp+AVxMNVWaHUElpUYC0TRRE= +github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1/go.mod h1:irE9ILwhE8VooWdhHhBy0qXzOMRlHKoWoMlr31mAFCE= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI= +github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= +github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= +github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= +github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= +github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= +github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huaweicloud/golangsdk v0.0.0-20200304081349-45ec0797f2a4/go.mod h1:WQBcHRNX9shz3928lWEvstQJtAtYI7ks6XlgtRT9Tcw= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4= +github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/manicminer/hamilton v0.32.0 h1:awVHVXOLp9P5QbWaOFqneuWjAvzAk3BYtTEX6AeWB5k= +github.com/manicminer/hamilton v0.32.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/oracle/oci-go-sdk v12.5.0+incompatible h1:pr08ECoaDKHWO9tnzJB1YqClEs7ZK1CFOez2DQocH14= +github.com/oracle/oci-go-sdk v12.5.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= +github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/yandex-cloud/go-genproto v0.0.0-20200722140432-762fe965ce77/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= +github.com/yandex-cloud/go-sdk v0.0.0-20200722140627-2194e5077f13/go.mod h1:LEdAMqa1v/7KYe4b13ALLkonuDxLph57ibUb50ctvJk= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok= +go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= +go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 h1:4Xw2NwItrJOFR5s6PnK98PI6Bgw1LhMP1j/rO5WP0S4= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/plugins/host/mains/azure/main.go b/plugins/host/mains/azure/main.go new file mode 100644 index 000000000..f1d22ead8 --- /dev/null +++ b/plugins/host/mains/azure/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "os" + + azhp "github.com/hashicorp/boundary-plugin-host-azure/plugin" + hp "github.com/hashicorp/boundary/sdk/plugins/host" + "github.com/hashicorp/go-hclog" +) + +func main() { + if err := hp.ServeHostPlugin(new(azhp.AzurePlugin), hp.WithLogger(hclog.NewNullLogger())); err != nil { + fmt.Println("Error serving plugin", err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/scripts/build.sh b/scripts/build.sh index 935563e66..edd91e553 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# This script builds the application from source for multiple platforms. +# This script builds the application from source for a single platform. set -e # Get the parent directory of where this script is. @@ -11,6 +11,10 @@ DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that directory cd "$DIR" +if [ "${CI_BUILD}x" != "x" ]; then + source /home/circleci/.bashrc +fi + # Set build tags BUILD_TAGS="${BUILD_TAGS:-"boundary"}" @@ -18,20 +22,13 @@ BUILD_TAGS="${BUILD_TAGS:-"boundary"}" GIT_COMMIT="$(git rev-parse HEAD)" GIT_DIRTY="$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)" -# If its dev mode, only build for ourself -if [ "${BOUNDARY_DEV_BUILD}x" != "x" ] && [ "${XC_OSARCH}x" == "x" ]; then - XC_OS=$(go env GOOS) - XC_ARCH=$(go env GOARCH) - XC_OSARCH=$(go env GOOS)/$(go env GOARCH) -elif [ "${XC_OSARCH}x" != "x" ]; then - IFS='/' read -ra SPLITXC <<< "${XC_OSARCH}" - DEV_PLATFORM="./pkg/${SPLITXC[0]}_${SPLITXC[1]}" +# If not explicitly cross-compiling, build for the current platform +if [ "${GOOS}x" == "x" ]; then + GOOS=$(go env GOOS) +fi +if [ "${GOARCH}x" == "x" ]; then + GOARCH=$(go env GOARCH) fi - -# Determine the arch/os combos we're building for -XC_ARCH=${XC_ARCH:-"386 amd64"} -XC_OS=${XC_OS:-linux darwin windows freebsd openbsd netbsd solaris} -XC_OSARCH=${XC_OSARCH:-"linux/386 linux/amd64 linux/arm linux/arm64 darwin/386 darwin/amd64 windows/386 windows/amd64 freebsd/386 freebsd/amd64 freebsd/arm openbsd/386 openbsd/amd64 openbsd/arm netbsd/386 netbsd/amd64 solaris/amd64"} GOPATH=${GOPATH:-$(go env GOPATH)} case $(uname) in @@ -40,50 +37,49 @@ case $(uname) in ;; esac +BINARY_SUFFIX="" +if [ "${GOOS}x" == "windowsx" ]; then + BINARY_SUFFIX=".exe" +fi + +# Build needed plugins first +ORIG_PATH=$(pwd); +echo "==> Building Host Plugins..." +for PLUGIN_TYPE in host; do + rm -f $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}* + for CURR_PLUGIN in $(ls $ORIG_PATH/plugins/$PLUGIN_TYPE/mains); do + cd $ORIG_PATH/plugins/$PLUGIN_TYPE/mains/$CURR_PLUGIN; + go build -v -o $ORIG_PATH/plugins/$PLUGIN_TYPE/assets/boundary-plugin-${PLUGIN_TYPE}-${CURR_PLUGIN}${BINARY_SUFFIX} .; + cd $ORIG_PATH; + done; + cd $ORIG_PATH/plugins/$PLUGIN_TYPE/assets; + for CURR_PLUGIN in $(ls); do + gzip -f -9 $CURR_PLUGIN; + done; + cd $ORIG_PATH; +done; + +if [ "${CI_BUILD}x" != "x" ]; then + exit +fi + # Delete the old dir echo "==> Removing old directory..." rm -f bin/* -rm -rf pkg/* mkdir -p bin/ # Build! -# If GOX_PARALLEL_BUILDS is set, it will be used to add a "-parallel=${GOX_PARALLEL_BUILDS}" gox parameter echo "==> Building..." -gox \ - -osarch="${XC_OSARCH}" \ - -gcflags "${GCFLAGS}" \ - -ldflags "${LD_FLAGS}-X github.com/hashicorp/boundary/version.GitCommit=${GIT_COMMIT}${GIT_DIRTY}" \ - -output "pkg/{{.OS}}_{{.Arch}}/boundary" \ - ${GOX_PARALLEL_BUILDS+-parallel="${GOX_PARALLEL_BUILDS}"} \ - -tags="${BUILD_TAGS}" \ +BINARY_NAME="boundary${BINARY_SUFFIX}" +go build -tags="${BUILD_TAGS}" \ + -ldflags "-X github.com/hashicorp/boundary/version.GitCommit=${GIT_COMMIT}${GIT_DIRTY}" \ + -o "bin/${BINARY_NAME}" \ ./cmd/boundary -# Move all the compiled things to the $GOPATH/bin -OLDIFS=$IFS -IFS=: MAIN_GOPATH=($GOPATH) -IFS=$OLDIFS -# Copy our OS/Arch to the bin/ directory -DEV_PLATFORM=${DEV_PLATFORM:-"./pkg/$(go env GOOS)_$(go env GOARCH)"} -for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do - cp -f ${F} bin/ - cp -f ${F} ${MAIN_GOPATH}/bin/ -done - -if [ "${BOUNDARY_DEV_BUILD}x" = "x" ]; then - # Zip and copy to the dist dir - echo "==> Packaging..." - for PLATFORM in $(find ./pkg -mindepth 1 -maxdepth 1 -type d); do - OSARCH=$(basename ${PLATFORM}) - echo "--> ${OSARCH}" - - pushd $PLATFORM >/dev/null 2>&1 - zip ../${OSARCH}.zip ./* - popd >/dev/null 2>&1 - done -fi +# Copy binary into gopath +echo "==> Copying binary into GOPATH" +cp -f "bin/${BINARY_NAME}" "${GOPATH}/bin/" # Done! -echo -echo "==> Results:" -ls -hl bin/ +echo "==> Done!" diff --git a/sdk/go.mod b/sdk/go.mod index 197901e1d..26ae4b3c2 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -9,7 +9,9 @@ require ( github.com/hashicorp/eventlogger v0.1.0 github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-kms-wrapping v0.6.6 + github.com/hashicorp/go-plugin v1.0.1 github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 diff --git a/sdk/go.sum b/sdk/go.sum index e2de2fcfe..761088bea 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -267,6 +267,7 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= @@ -310,6 +311,7 @@ github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VK github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -368,6 +370,7 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -387,6 +390,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/sdk/plugins/host/load.go b/sdk/plugins/host/load.go new file mode 100644 index 000000000..ac55017ff --- /dev/null +++ b/sdk/plugins/host/load.go @@ -0,0 +1,213 @@ +package external_host_plugins + +import ( + "bytes" + "compress/gzip" + "context" + "fmt" + "io/fs" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + + pb "github.com/hashicorp/boundary/sdk/pbs/plugin" +) + +// NOTE: This package could probably use some reflect based bits to allow +// loading other types of plugins later. That is an exercise left for future +// refactoring, but worth calling out now. + +type pluginInfo struct { + containerFs fs.FS + filename string + creationFunc func() (pb.HostPluginServiceClient, error) +} + +// CreateHostPlugin takes in a type, parses the various options to look for a +// plugin matching that name, and returns a host plugin client, a cleanup +// function to execute on shutdown of the enclosing program, and an error. +func CreateHostPlugin( + ctx context.Context, + pluginType string, + opt ...Option, +) ( + hp pb.HostPluginServiceClient, + cleanup func() error, + retErr error, +) { + defer func() { + if retErr != nil && cleanup != nil { + _ = cleanup() + } + }() + + pluginType = strings.ToLower(pluginType) + + opts, err := getOpts(opt...) + if err != nil { + return nil, nil, fmt.Errorf("error parsing host plugin options: %w", err) + } + if len(opts.withHostPluginsSources) == 0 { + return nil, nil, fmt.Errorf("no host plugins available") + } + + // First, scan available plugins, then find the right one to use + pluginMap := map[string]pluginInfo{} + var plugin pluginInfo + { + for _, sourceInfo := range opts.withHostPluginsSources { + switch { + case sourceInfo.pluginFs != nil: + dirs, err := fs.ReadDir(sourceInfo.pluginFs, ".") + if err != nil { + return nil, nil, fmt.Errorf("error scanning host plugins: %w", err) + } + // Store a match between the config type string and the expected plugin name + for _, entry := range dirs { + pluginType := strings.TrimSuffix(strings.TrimPrefix(entry.Name(), sourceInfo.pluginFsPrefix), ".gz") + if runtime.GOOS == "windows" { + pluginType = strings.TrimSuffix(pluginType, ".exe") + } + pluginMap[pluginType] = pluginInfo{ + containerFs: sourceInfo.pluginFs, + filename: entry.Name(), + } + } + case sourceInfo.pluginMap != nil: + for k, creationFunc := range sourceInfo.pluginMap { + pluginMap[k] = pluginInfo{creationFunc: creationFunc, filename: k} + } + } + } + + plugin = pluginMap[pluginType] + if plugin.filename == "" && plugin.creationFunc == nil { + return nil, nil, fmt.Errorf("unknown host plugin type %q", pluginType) + } + } + + // If the source is just a func, execute it and skip ahead; otherwise it's a plugin, so instantiate it + { + switch { + case plugin.creationFunc != nil: + hp, err = plugin.creationFunc() + if err != nil { + return nil, nil, fmt.Errorf("error performing direct instantiation of host plugin with type %q: %w", plugin.filename, err) + } + + case plugin.containerFs != nil: + hp, cleanup, err = executeHostPlugin(plugin, opt...) + if err != nil { + return nil, cleanup, err + } + } + } + + return hp, cleanup, nil +} + +// executeHostPlugin takes in the discovered plugin information and spins out +// the actual binary, returning a client that talks to it. +func executeHostPlugin(plugin pluginInfo, opt ...Option) (pb.HostPluginServiceClient, func() error, error) { + opts, err := getOpts(opt...) + if err != nil { + return nil, nil, fmt.Errorf("error parsing host plugin options: %w", err) + } + + // Open and basic validation + file, err := plugin.containerFs.Open(plugin.filename) + if err != nil { + return nil, nil, err + } + stat, err := file.Stat() + if err != nil { + return nil, nil, fmt.Errorf("error discovering host plugin information: %w", err) + } + if stat.IsDir() { + return nil, nil, fmt.Errorf("host plugin is a directory, not a file") + } + + // Read in plugin bytes + expLen := stat.Size() + buf := make([]byte, expLen) + readLen, err := file.Read(buf) + if err != nil { + file.Close() + return nil, nil, fmt.Errorf("error reading host plugin bytes: %w", err) + } + if err := file.Close(); err != nil { + return nil, nil, fmt.Errorf("error closing host plugin file: %w", err) + } + if int64(readLen) != expLen { + return nil, nil, fmt.Errorf("reading host plugin expected %d bytes, read %d", expLen, readLen) + } + + executedFileName := plugin.filename + + // If it's compressed, uncompress it + if strings.HasSuffix(plugin.filename, ".gz") { + executedFileName = strings.TrimSuffix(plugin.filename, ".gz") + gzipReader, err := gzip.NewReader(bytes.NewReader(buf)) + if err != nil { + return nil, nil, fmt.Errorf("error creating gzip decompression reader: %w", err) + } + uncompBuf := new(bytes.Buffer) + _, err = uncompBuf.ReadFrom(gzipReader) + gzipReader.Close() + if err != nil { + return nil, nil, fmt.Errorf("error reading gzip compressed data from reader: %w", err) + } + buf = uncompBuf.Bytes() + } + + cleanup := func() error { + return nil + } + + // Now, create a temp dir and write out the plugin bytes + dir := opts.withHostPluginExecutionDir + if dir == "" { + tmpDir, err := ioutil.TempDir("", "*") + if err != nil { + return nil, nil, fmt.Errorf("error creating tmp dir for kms execution: %w", err) + } + cleanup = func() error { + return os.RemoveAll(tmpDir) + } + dir = tmpDir + } + pluginPath := filepath.Join(dir, executedFileName) + if err := ioutil.WriteFile(pluginPath, buf, fs.FileMode(0700)); err != nil { + return nil, cleanup, fmt.Errorf("error writing out host plugin for execution: %w", err) + } + + // Execute the plugin + client, err := NewHostPluginClient(pluginPath, WithLogger(opts.withLogger)) + if err != nil { + return nil, cleanup, fmt.Errorf("error fetching host plugin client: %w", err) + } + origCleanup := cleanup + cleanup = func() error { + client.Kill() + return origCleanup() + } + rpcClient, err := client.Client() + if err != nil { + return nil, cleanup, fmt.Errorf("error fetching host plugin rpc client: %w", err) + } + + raw, err := rpcClient.Dispense(hostServicePluginSetName) + if err != nil { + return nil, cleanup, fmt.Errorf("error dispensing host plugin: %w", err) + } + + var ok bool + hp, ok := raw.(pb.HostPluginServiceClient) + if !ok { + return nil, cleanup, fmt.Errorf("error converting rpc host plugin to host plugin interface: %w", err) + } + + return hp, cleanup, nil +} diff --git a/sdk/plugins/host/options.go b/sdk/plugins/host/options.go new file mode 100644 index 000000000..2046c2355 --- /dev/null +++ b/sdk/plugins/host/options.go @@ -0,0 +1,119 @@ +package external_host_plugins + +import ( + "errors" + "fmt" + "io/fs" + "os" + + pb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/hashicorp/go-hclog" +) + +// getOpts iterates the inbound Options and returns a struct +func getOpts(opt ...Option) (*options, error) { + opts := getDefaultOptions() + for _, o := range opt { + if o == nil { + continue + } + if err := o(opts); err != nil { + return nil, fmt.Errorf("error running option function: %w", err) + } + } + return opts, nil +} + +// Option - a type that wraps an interface for compile-time safety but can +// contain an option for this package or for wrappers implementing this +// interface. +type Option func(*options) error + +type options struct { + withHostPluginsSources []pluginSourceInfo + withHostPluginExecutionDir string + withLogger hclog.Logger +} + +func getDefaultOptions() *options { + return &options{} +} + +// pluginSourceInfo contains information about plugin sources. Each entry in the +// final slice of withHostPluginsSources will either have the Fs/FsPrefix values +// populated or have the map populated. The former is for loading and executing +// a plugin via go-plugin from the filesystem; the latter is for executing it +// directly in-memory. +type pluginSourceInfo struct { + pluginMap map[string]func() (pb.HostPluginServiceClient, error) + pluginFs fs.FS + pluginFsPrefix string +} + +// WithLogger allows passing a logger to the plugin library for debugging +func WithLogger(logger hclog.Logger) Option { + return func(o *options) error { + o.withLogger = logger + return nil + } +} + +// WithHostPluginsFilesystem provides an fs.FS containing plugins that can be +// executed to provide Host functionality. This can be specified multiple times; +// all FSes will be scanned. If there are conflicts, the last one wins (this +// property is shared with WithHostPluginsMap). The prefix will be stripped from +// each entry when determining the plugin type. +func WithHostPluginsFilesystem(prefix string, plugins fs.FS) Option { + return func(o *options) error { + if plugins == nil { + return errors.New("nil host plugin filesystem passed into option") + } + o.withHostPluginsSources = append(o.withHostPluginsSources, + pluginSourceInfo{ + pluginFs: plugins, + pluginFsPrefix: prefix, + }, + ) + return nil + } +} + +// WithHostPluginsMap provides a map containing functions that can be called to +// provide implementations of the server. This can be specified multiple times; +// all FSes will be scanned. If there are conflicts, the last one wins (this +// property is shared with WithHostPluginsFilesystem). +func WithHostPluginsMap(plugins map[string]func() (pb.HostPluginServiceClient, error)) Option { + return func(o *options) error { + if len(plugins) == 0 { + return errors.New("no entries in host plugins map passed into option") + } + o.withHostPluginsSources = append(o.withHostPluginsSources, + pluginSourceInfo{ + pluginMap: plugins, + }, + ) + return nil + } +} + +// WithHostPluginExecutionDir allows setting a specific directory for +// writing out and executing plugins; if not set, os.TempDir will be used +// to create a suitable directory. +func WithHostPluginExecutionDir(dir string) Option { + return func(o *options) error { + if dir == "" { + // We always call this with the option, so if it's not actually set, + // don't error + return nil + } + fi, err := os.Stat(dir) + if err != nil { + return fmt.Errorf("error while performing stat to validate path %q is a directory: %w", dir, err) + } + if !fi.IsDir() { + return fmt.Errorf("given plugin execution path %q is not a directory", dir) + } + o.withHostPluginExecutionDir = dir + return nil + } +} diff --git a/sdk/plugins/host/plugin.go b/sdk/plugins/host/plugin.go new file mode 100644 index 000000000..6b44db321 --- /dev/null +++ b/sdk/plugins/host/plugin.go @@ -0,0 +1,89 @@ +package external_host_plugins + +import ( + "context" + "fmt" + "os/exec" + + pb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" +) + +const ( + hostServicePluginSetName = "host-plugin" +) + +// HandshakeConfig is a shared config that can be used regardless of plugin, to +// avoid having to know type-specific things about each plugin +var HandshakeConfig = plugin.HandshakeConfig{ + MagicCookieKey: "HASHICORP_BOUNDARY_HOST_PLUGIN", + MagicCookieValue: hostServicePluginSetName, +} + +// ServeHostPlugin is a generic function to start serving a host plugin service as a +// plugin +func ServeHostPlugin(svc pb.HostPluginServiceServer, opt ...Option) error { + opts, err := getOpts(opt...) + if err != nil { + return err + } + hostServiceServer, err := NewHostPluginServiceServer(svc) + if err != nil { + return err + } + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: HandshakeConfig, + VersionedPlugins: map[int]plugin.PluginSet{ + 1: {hostServicePluginSetName: hostServiceServer}, + }, + Logger: opts.withLogger, + GRPCServer: plugin.DefaultGRPCServer, + }) + return nil +} + +type hostPlugin struct { + plugin.Plugin + + impl pb.HostPluginServiceServer +} + +func NewHostPluginServiceServer(impl pb.HostPluginServiceServer) (*hostPlugin, error) { + if impl == nil { + return nil, fmt.Errorf("empty underlying host plugin passed in") + } + return &hostPlugin{ + impl: impl, + }, nil +} + +func NewHostPluginClient(pluginPath string, opt ...Option) (*plugin.Client, error) { + opts, err := getOpts(opt...) + if err != nil { + return nil, err + } + hostServiceClient := &hostPlugin{} + + return plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: HandshakeConfig, + VersionedPlugins: map[int]plugin.PluginSet{ + 1: {hostServicePluginSetName: hostServiceClient}, + }, + Cmd: exec.Command(pluginPath), + AllowedProtocols: []plugin.Protocol{ + plugin.ProtocolGRPC, + }, + Logger: opts.withLogger, + AutoMTLS: true, + }), nil +} + +func (h *hostPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + pb.RegisterHostPluginServiceServer(s, h.impl) + return nil +} + +func (h *hostPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return pb.NewHostPluginServiceClient(c), nil +} From e8313146f6c484c2ef7816fda81e56e4ca38b8e0 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 20 Oct 2021 13:49:46 -0400 Subject: [PATCH 34/71] Update plugin dependencies --- plugins/host/mains/aws/go.mod | 4 ++-- plugins/host/mains/aws/go.sum | 32 ++++++++++++++++++-------------- plugins/host/mains/azure/go.mod | 4 ++-- plugins/host/mains/azure/go.sum | 9 ++++----- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod index 6ee93a4cf..2262ae32e 100644 --- a/plugins/host/mains/aws/go.mod +++ b/plugins/host/mains/aws/go.mod @@ -8,8 +8,8 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e - github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-plugin v1.4.3 // indirect golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum index 3db4f374e..d46954112 100644 --- a/plugins/host/mains/aws/go.sum +++ b/plugins/host/mains/aws/go.sum @@ -108,8 +108,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.40.44 h1:kECaYybTWYZY5IKHvQMxbE6Wi5Qrb+7hbkV7zQV3Sg8= -github.com/aws/aws-sdk-go v1.40.44/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.40.55 h1:l5xNBmafEAfdJIIe32QbTLG3j/JscH8fzZqYyMlntuI= +github.com/aws/aws-sdk-go v1.40.55/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -162,8 +162,9 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -257,11 +258,10 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e h1:7MimW+cEw3Hcr4NMQe3M8jJHYW40Y9oTSR6zyHzs31E= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20210930164321-cb00f0cb7a9e/go.mod h1:TbqGUjL8yDVqdoEHCiEPoE7lv5yfQRKxqPinfGUeC30= -github.com/hashicorp/boundary/sdk v0.0.11-0.20210926154843-a834a147850b/go.mod h1:ZkKPk/KyB4sMF9awLNQMBBS1I+8E/K3Ap5uPe36sGhM= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 h1:LYB55XVeUZUJXH/UU51BY+s7/z4BlLlc9NemlTFFlrE= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57 h1:EQmv83A1CTmgeLQPDQ0sRALw2ou5KWmVZ7k/vSqmxII= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57/go.mod h1:YZdE92O3RfToEukkgwmobOsW0lqP9obE5qz/MYnJZ5A= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 h1:uiFO3cBZ/y/5XA+gF6wp9OdrIkiZA9mS7ZHLpl3SDsM= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -304,8 +304,8 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 h1:aCPwC2WPyomaw5gk5ZZ7DPevN8d9DS3igkmq6DxGMjU= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7mduW2Q+2FMrXawkLM8e7NU= github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ= @@ -386,14 +386,17 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LE github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= @@ -681,8 +684,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef h1:fPxZ3Umkct3LZ8gK9nbk+DWDJ9fstZa2grBn+lWVKPs= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/plugins/host/mains/azure/go.mod b/plugins/host/mains/azure/go.mod index 482fe9d58..6c58f1953 100644 --- a/plugins/host/mains/azure/go.mod +++ b/plugins/host/mains/azure/go.mod @@ -3,8 +3,8 @@ module github.com/hashicorp/boundary/plugins/host/mains/azure go 1.16 require ( - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f - github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-plugin v1.4.3 // indirect ) diff --git a/plugins/host/mains/azure/go.sum b/plugins/host/mains/azure/go.sum index 245f88f14..90b8f2cb2 100644 --- a/plugins/host/mains/azure/go.sum +++ b/plugins/host/mains/azure/go.sum @@ -255,11 +255,10 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f h1:F0xrvXzIxKlRPjbrGjHGvqMuC4+txANqQBtnbjniVBc= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211007161259-8f06ca9bc48f/go.mod h1:0oG9826pkbPn9Aq0PSyKTn2WVxCP4ruaMUulVTplSI8= -github.com/hashicorp/boundary/sdk v0.0.11-0.20210928201932-8b1ca4b0b8b9/go.mod h1:ZkKPk/KyB4sMF9awLNQMBBS1I+8E/K3Ap5uPe36sGhM= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370 h1:LYB55XVeUZUJXH/UU51BY+s7/z4BlLlc9NemlTFFlrE= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211006192840-ea88a3d81370/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1 h1:HJeBFTmjWY76RF53PV6RuDGwskaHzkJ5xsoAN9MvlbY= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1/go.mod h1:aXK+G8Cd3TmfGJoSepevgckCkmHYW5tkMw4WI54eP3w= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 h1:uiFO3cBZ/y/5XA+gF6wp9OdrIkiZA9mS7ZHLpl3SDsM= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 23baaac36b7bdb5113bedb333fb33de9aa53d991 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 20 Oct 2021 12:20:28 -0700 Subject: [PATCH 35/71] Add DB changes for host, set members, and host addresses for plugins. (#1584) * Add DB changes for host, set members, and host addresses for plugins. * Add priority, address review feedback, update tests, naming, protos Co-authored-by: Jeff Mitchell Co-authored-by: Michael Gaffney --- .../oss/postgres/17/01_domain_types.up.sql | 18 + .../17/{01_plugin.up.sql => 02_plugin.up.sql} | 0 ...hostplugin.up.sql => 03_hostplugin.up.sql} | 0 .../17/{03_host.up.sql => 04_host.up.sql} | 0 ...ugin_host.up.sql => 05_plugin_host.up.sql} | 209 ++++++- ...s.up.sql => 06_preferred_endpoints.up.sql} | 4 +- ...up.sql => 07_wh_session_dimensions.up.sql} | 0 internal/host/host.go | 2 + internal/host/host_dns_address.go | 76 +++ internal/host/host_ip_address.go | 81 +++ internal/host/plugin/host.go | 149 ++++- internal/host/plugin/host_address_test.go | 548 +++++++++++++++++ internal/host/plugin/host_test.go | 75 ++- internal/host/plugin/options.go | 34 ++ internal/host/plugin/options_test.go | 24 + internal/host/plugin/repository_host.go | 284 +++++++++ internal/host/plugin/repository_host_set.go | 11 +- .../host/plugin/repository_host_set_test.go | 7 +- internal/host/plugin/repository_host_test.go | 245 ++++++++ internal/host/plugin/store/host.pb.go | 557 +++++++++++------- internal/host/plugin/testing.go | 65 ++ internal/host/static/host.go | 10 + .../host/static/repository_host_set_member.go | 3 + internal/host/store/host.pb.go | 175 +++++- .../storage/host/plugin/store/v1/host.proto | 94 +-- .../storage/host/store/v1/host.proto | 22 + 26 files changed, 2341 insertions(+), 352 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql rename internal/db/schema/migrations/oss/postgres/17/{01_plugin.up.sql => 02_plugin.up.sql} (100%) rename internal/db/schema/migrations/oss/postgres/17/{02_hostplugin.up.sql => 03_hostplugin.up.sql} (100%) rename internal/db/schema/migrations/oss/postgres/17/{03_host.up.sql => 04_host.up.sql} (100%) rename internal/db/schema/migrations/oss/postgres/17/{04_plugin_host.up.sql => 05_plugin_host.up.sql} (56%) rename internal/db/schema/migrations/oss/postgres/17/{05_preferred_endpoints.up.sql => 06_preferred_endpoints.up.sql} (95%) rename internal/db/schema/migrations/oss/postgres/17/{06_wh_session_dimensions.up.sql => 07_wh_session_dimensions.up.sql} (100%) create mode 100644 internal/host/host_dns_address.go create mode 100644 internal/host/host_ip_address.go create mode 100644 internal/host/plugin/host_address_test.go create mode 100644 internal/host/plugin/repository_host.go create mode 100644 internal/host/plugin/repository_host_test.go diff --git a/internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql b/internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql new file mode 100644 index 000000000..0686914e9 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql @@ -0,0 +1,18 @@ +begin; + +create domain wt_priority as int not null + constraint priority_must_be_greater_than_zero + check(value > 0); +comment on domain wt_priority is +'Represents a priority value which must not be null and must be greater than zero'; + +-- wt_dns_name defines a type for dns names +create domain wt_dns_name as text not null + constraint wt_dns_name_too_short + check (length(trim(value)) > 0) + constraint wt_dns_name_too_long + check (length(trim(value)) < 256); +comment on domain wt_dns_name is +'standard column for dns names'; + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres/17/01_plugin.up.sql b/internal/db/schema/migrations/oss/postgres/17/02_plugin.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/01_plugin.up.sql rename to internal/db/schema/migrations/oss/postgres/17/02_plugin.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/02_hostplugin.up.sql b/internal/db/schema/migrations/oss/postgres/17/03_hostplugin.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/02_hostplugin.up.sql rename to internal/db/schema/migrations/oss/postgres/17/03_hostplugin.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/03_host.up.sql b/internal/db/schema/migrations/oss/postgres/17/04_host.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/03_host.up.sql rename to internal/db/schema/migrations/oss/postgres/17/04_host.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/04_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/17/05_plugin_host.up.sql similarity index 56% rename from internal/db/schema/migrations/oss/postgres/17/04_plugin_host.up.sql rename to internal/db/schema/migrations/oss/postgres/17/05_plugin_host.up.sql index 1ca7386f2..9d0855026 100644 --- a/internal/db/schema/migrations/oss/postgres/17/04_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/17/05_plugin_host.up.sql @@ -56,10 +56,10 @@ begin; version wt_version, attributes bytea not null, constraint host_catalog_fkey - foreign key (scope_id, public_id) - references host_catalog (scope_id, public_id) - on delete cascade - on update cascade, + foreign key (scope_id, public_id) + references host_catalog (scope_id, public_id) + on delete cascade + on update cascade, constraint host_plugin_catalog_scope_id_name_uq unique(scope_id, name) ); @@ -128,10 +128,10 @@ begin; constraint host_plugin_set_catalog_id_name_uq unique(catalog_id, name), constraint host_set_fkey - foreign key (catalog_id, public_id) - references host_set (catalog_id, public_id) - on delete cascade - on update cascade, + foreign key (catalog_id, public_id) + references host_set (catalog_id, public_id) + on delete cascade + on update cascade, constraint host_plugin_set_catalog_id_public_id_uq unique(catalog_id, public_id) ); @@ -154,10 +154,7 @@ begin; create trigger delete_host_set_subtype after delete on host_plugin_set for each row execute procedure delete_host_set_subtype(); - - -- TODO: Rebuild this table to appropriately cache or sync data from the - -- plugin'ed service. - -- host_plugin_host captures plugin based host data. This is only written to +-- host_plugin_host captures plugin based host data. This is only written to -- from the controller and is not mutable directly by actions from the end -- user. create table host_plugin_host ( @@ -167,37 +164,28 @@ begin; references host_plugin_catalog (public_id) on delete cascade on update cascade, + external_id text not null + constraint external_id_must_not_be_empty + check(length(trim(external_id)) > 0), name wt_name, description text, create_time wt_timestamp, + -- update_time is the last time the data was synced with what is provided + -- from the plugin. update_time wt_timestamp, - version wt_version, - - -- TODO: Break out the address to match the domain model where a host - -- can have multiple addresses and relies on the set to select the - -- prefered one. - address text not null - constraint address_must_be_more_than_2_characters - check(length(trim(address)) > 2) - constraint address_must_be_less_than_256_characters - check(length(trim(address)) < 256), - constraint host_fkey foreign key (catalog_id, public_id) references host (catalog_id, public_id) on delete cascade on update cascade, - constraint host_plugin_host_catalog_id_name_uq unique(catalog_id, name), - + constraint host_plugin_host_catalog_id_external_id_uq + unique(catalog_id, external_id), constraint host_plugin_host_catalog_id_public_id_uq unique(catalog_id, public_id) ); - create trigger update_version_column after update on host_plugin_host - for each row execute procedure update_version_column(); - create trigger update_time_column before update on host_plugin_host for each row execute procedure update_time_column(); @@ -205,7 +193,7 @@ begin; for each row execute procedure default_create_time(); create trigger immutable_columns before update on host_plugin_host - for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + for each row execute procedure immutable_columns('public_id', 'catalog_id', 'external_id', 'create_time'); create trigger insert_host_subtype before insert on host_plugin_host for each row execute procedure insert_host_subtype(); @@ -213,6 +201,141 @@ begin; create trigger delete_host_subtype after delete on host_plugin_host for each row execute procedure delete_host_subtype(); + -- host_ip_address contains the IP addresses associated with + -- a host, one per row. + create table host_ip_address ( + host_id wt_public_id + constraint host_fkey + references host(public_id) + on delete cascade + on update cascade, + priority wt_priority, + address inet not null, + create_time wt_timestamp, + primary key (host_id, priority), + constraint host_ip_address_host_id_address_uq + unique(host_id, address) + ); + comment on table host_ip_address is + 'host_ip_address entries are ip addresses set on a host with a preserved order.'; + + create trigger default_create_time_column before insert on host_ip_address + for each row execute procedure default_create_time(); + + -- host_immutable_ip_address() ensures that ip addresses assigned to hosts are + -- immutable. + create function + host_immutable_ip_address() + returns trigger + as $$ + begin + raise exception 'host ip addresses are immutable'; + end; + $$ language plpgsql; + + create trigger immutable_ip_address + before update on host_ip_address + for each row execute procedure host_immutable_ip_address(); + + -- host_dns_name contains the DNS names associated with a host, one per row. + create table host_dns_name ( + host_id wt_public_id + constraint host_fkey + references host(public_id) + on delete cascade + on update cascade, + priority wt_priority, + name wt_dns_name, + create_time wt_timestamp, + primary key (host_id, priority), + constraint host_dns_name_host_id_name_uq + unique(host_id, name) + ); + comment on table host_dns_name is + 'host_dns_name entries are dns names set on a host with a preserved order.'; + + create trigger default_create_time_column before insert on host_dns_name + for each row execute procedure default_create_time(); + + -- host_immutable_dns_name() ensures that dns names assigned to hosts are + -- immutable. + create function + host_immutable_dns_name() + returns trigger + as $$ + begin + raise exception 'host dns names are immutable'; + end; + $$ language plpgsql; + + create trigger immutable_dns_name + before update on host_dns_name + for each row execute procedure host_immutable_dns_name(); + + create table host_plugin_set_member ( + host_id wt_public_id not null, + set_id wt_public_id not null, + catalog_id wt_public_id not null, + create_time wt_timestamp, + primary key(host_id, set_id), + constraint host_plugin_host_fkey + foreign key (catalog_id, host_id) + references host_plugin_host (catalog_id, public_id) + on delete cascade + on update cascade, + constraint host_plugin_set_fkey + foreign key (catalog_id, set_id) + references host_plugin_set (catalog_id, public_id) + on delete cascade + on update cascade + ); + comment on table host_plugin_set_member is + 'host_plugin_set_member entries are the membership relationships from plugin hosts in plugin sets.'; + + create trigger default_create_time_column before insert on host_plugin_set_member + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on host_plugin_set_member + for each row execute procedure immutable_columns('host_id', 'set_id', 'catalog_id', 'create_time'); + + create function insert_host_plugin_set_member() + returns trigger + as $$ + begin + select host_plugin_set.catalog_id + into new.catalog_id + from host_plugin_set + where host_plugin_set.public_id = new.set_id; + return new; + end; + $$ language plpgsql; + comment on function insert_host_plugin_set_member is + 'insert_host_plugin_set_member entries are the membership relationships from plugin hosts in plugin sets.'; + + create trigger insert_host_plugin_set_member before insert on host_plugin_set_member + for each row execute procedure insert_host_plugin_set_member(); + + -- delete_orphaned_host_plugin_host is an after delete trigger + -- function to delete plugin hosts when no more set members + -- reference it. + create function delete_orphaned_host_plugin_host() + returns trigger + as $$ + begin + delete from host_plugin_host + where not exists (select host_id + from host_plugin_set_member + where host_id = old.host_id) + and + public_id = old.host_id; + return null; + end; + $$ language plpgsql; + comment on function delete_orphaned_host_plugin_host is + 'delete_orphaned_host_plugin_host deletes a host when all set members with that host are deleted.'; + + create trigger delete_orphaned_host_plugin_host after delete on host_plugin_set_member + for each row execute procedure delete_orphaned_host_plugin_host(); insert into oplog_ticket (name, version) values @@ -220,4 +343,32 @@ begin; ('host_plugin_catalog_secret', 1), ('host_plugin_set', 1); + + -- host_plugin_host_with_value_obj is useful for reading a plugin host with + -- its associated value objects (ip addresses, dns names, set membership) as + -- columns with delimited values. The delimiter depends on the value objects + -- (e.g. if they need ordering). + create view host_plugin_host_with_value_obj as + select + h.public_id, + h.catalog_id, + h.external_id, + hc.plugin_id, + h.name, + h.description, + h.create_time, + h.update_time, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct concat_ws('=', hip.priority, hip.address), '|') as ip_addresses, + string_agg(distinct concat_ws('=', hdns.priority, hdns.name), '|') as dns_names + from + host_plugin_host h + join host_plugin_catalog hc on h.catalog_id = hc.public_id + left outer join host_ip_address hip on h.public_id = hip.host_id + left outer join host_dns_name hdns on h.public_id = hdns.host_id + -- FIXME: add set membership once that's shaken out + group by h.public_id, hc.plugin_id; + comment on view host_plugin_host_with_value_obj is + 'host plugin host with its associated value objects'; + commit; diff --git a/internal/db/schema/migrations/oss/postgres/17/05_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/17/06_preferred_endpoints.up.sql similarity index 95% rename from internal/db/schema/migrations/oss/postgres/17/05_preferred_endpoints.up.sql rename to internal/db/schema/migrations/oss/postgres/17/06_preferred_endpoints.up.sql index eafa2c5e8..aedabd48e 100644 --- a/internal/db/schema/migrations/oss/postgres/17/05_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/oss/postgres/17/06_preferred_endpoints.up.sql @@ -7,9 +7,7 @@ create table host_set_preferred_endpoint ( references host_set(public_id) on delete cascade on update cascade, - priority int not null - constraint priority_must_be_greater_than_zero - check(priority > 0), + priority wt_priority, condition text not null constraint condition_must_not_be_too_short check(length(trim(condition)) > 4) -- minimum is 'dns:*' diff --git a/internal/db/schema/migrations/oss/postgres/17/06_wh_session_dimensions.up.sql b/internal/db/schema/migrations/oss/postgres/17/07_wh_session_dimensions.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/06_wh_session_dimensions.up.sql rename to internal/db/schema/migrations/oss/postgres/17/07_wh_session_dimensions.up.sql diff --git a/internal/host/host.go b/internal/host/host.go index 02bc57963..1c54995fb 100644 --- a/internal/host/host.go +++ b/internal/host/host.go @@ -20,4 +20,6 @@ type Host interface { boundary.Resource GetCatalogId() string GetAddress() string + GetIpAddresses() []string + GetDnsNames() []string } diff --git a/internal/host/host_dns_address.go b/internal/host/host_dns_address.go new file mode 100644 index 000000000..a038478b0 --- /dev/null +++ b/internal/host/host_dns_address.go @@ -0,0 +1,76 @@ +package host + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/store" + "google.golang.org/protobuf/proto" +) + +// NOTE: Tests for this are in the plugin subtype directory in host_address_test.go + +const defaultDnsNameTableName = "host_dns_name" + +type DnsName struct { + *store.DnsName + tableName string `gorm:"-"` +} + +func NewDnsName(ctx context.Context, hostId string, priority uint32, name string) (*DnsName, error) { + const op = "host.NewDnsName" + dn := &DnsName{ + DnsName: &store.DnsName{ + HostId: hostId, + Name: name, + Priority: priority, + }, + } + if err := dn.validate(ctx, op); err != nil { + return nil, err + } + return dn, nil +} + +// validate the host dns name. On success, it will return nil. +func (dn *DnsName) validate(ctx context.Context, caller errors.Op) error { + if dn.HostId == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing host id") + } + if dn.Priority < 1 { + return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value") + } + if dn.Name == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing dns name") + } + + return nil +} + +// allocDnsName make an empty one in memory. +func allocDnsName() DnsName { + return DnsName{ + DnsName: &store.DnsName{}, + } +} + +// Clone an DnsName +func (c *DnsName) Clone() *DnsName { + cp := proto.Clone(c.DnsName) + return &DnsName{ + DnsName: cp.(*store.DnsName), + } +} + +// TableName returns the table name. +func (c *DnsName) TableName() string { + if c.tableName != "" { + return c.tableName + } + return defaultDnsNameTableName +} + +// SetTableName sets the table name. +func (c *DnsName) SetTableName(n string) { + c.tableName = n +} diff --git a/internal/host/host_ip_address.go b/internal/host/host_ip_address.go new file mode 100644 index 000000000..d0644276e --- /dev/null +++ b/internal/host/host_ip_address.go @@ -0,0 +1,81 @@ +package host + +import ( + "context" + "net" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/store" + "google.golang.org/protobuf/proto" +) + +// NOTE: Tests for this are in the plugin subtype directory in host_address_test.go + +const defaultIpAddressTableName = "host_ip_address" + +type IpAddress struct { + *store.IpAddress + tableName string `gorm:"-"` +} + +func NewIpAddress(ctx context.Context, hostId string, priority uint32, address string) (*IpAddress, error) { + const op = "host.NewIpAddress" + + ia := &IpAddress{ + IpAddress: &store.IpAddress{ + HostId: hostId, + Address: address, + Priority: priority, + }, + } + if err := ia.validate(ctx, op); err != nil { + return nil, err + } + return ia, nil +} + +// validate the host ip address. On success, it will return nil. +func (ia *IpAddress) validate(ctx context.Context, caller errors.Op) error { + if ia.HostId == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing host id") + } + if ia.Priority < 1 { + return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value") + } + if ia.Address == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing ip address") + } + if ip := net.ParseIP(ia.Address); ip == nil { + return errors.New(ctx, errors.InvalidParameter, caller, "given address is not an ip address") + } + + return nil +} + +// allocIpAddress make an empty one in memory. +func allocIpAddress() IpAddress { + return IpAddress{ + IpAddress: &store.IpAddress{}, + } +} + +// Clone an IpAddress +func (c *IpAddress) Clone() *IpAddress { + cp := proto.Clone(c.IpAddress) + return &IpAddress{ + IpAddress: cp.(*store.IpAddress), + } +} + +// TableName returns the table name. +func (c *IpAddress) TableName() string { + if c.tableName != "" { + return c.tableName + } + return defaultIpAddressTableName +} + +// SetTableName sets the table name. +func (c *IpAddress) SetTableName(n string) { + c.tableName = n +} diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go index 96b431dfb..197eacef9 100644 --- a/internal/host/plugin/host.go +++ b/internal/host/plugin/host.go @@ -2,9 +2,14 @@ package plugin import ( "context" + "fmt" + "sort" + "strconv" + "strings" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" - "github.com/hashicorp/boundary/internal/oplog" "google.golang.org/protobuf/proto" ) @@ -14,27 +19,42 @@ import ( // field for this host's host catalog. type Host struct { *store.Host + PluginId string `gorm:"-"` tableName string `gorm:"-"` } // newHost creates a new in memory Host assigned to catalogId with an address. -// Name and description are the only valid options. All other options are -// ignored. -func newHost(ctx context.Context, catalogId, address string, opt ...Option) *Host { +// Supported options: WithName, WithDescription, WithIpAddresses, WithDnsNames, +// WithPluginId. Others ignored. +func newHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host { opts := getOpts(opt...) h := &Host{ + PluginId: opts.withPluginId, Host: &store.Host{ CatalogId: catalogId, + ExternalId: externalId, Name: opts.withName, Description: opts.withDescription, - Address: address, }, } + if len(opts.withIpAddresses) > 0 { + h.IpAddresses = make([]string, 0, len(opts.withIpAddresses)) + h.IpAddresses = append(h.IpAddresses, opts.withIpAddresses...) + } + if len(opts.withDnsNames) > 0 { + h.DnsNames = make([]string, 0, len(opts.withDnsNames)) + h.DnsNames = append(h.DnsNames, opts.withDnsNames...) + } return h } +// For compatibility with the general Host type +func (h *Host) GetAddress() string { + return "" +} + // TableName returns the table name for the host set. func (s *Host) TableName() string { if s.tableName != "" { @@ -55,22 +75,111 @@ func allocHost() *Host { } } -func (s *Host) clone() *Host { - cp := proto.Clone(s.Host) - hs := &Host{ - Host: cp.(*store.Host), +func (h *Host) clone() *Host { + cp := proto.Clone(h.Host) + return &Host{ + PluginId: h.PluginId, + Host: cp.(*store.Host), } - return hs } -func (s *Host) oplog(op oplog.OpType) oplog.Metadata { - metadata := oplog.Metadata{ - "resource-public-id": []string{s.PublicId}, - "resource-type": []string{"plugin-host"}, - "op-type": []string{op.String()}, - } - if s.CatalogId != "" { - metadata["catalog-id"] = []string{s.CatalogId} - } - return metadata +// hostAgg is a view that aggregates the host's value objects in to +// string fields delimited with the aggregateDelimiter of "|" +type hostAgg struct { + PublicId string `gorm:"primary_key"` + CatalogId string + ExternalId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + IpAddresses string + DnsNames string +} + +func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { + const op = "plugin.(hostAgg).toHost" + const aggregateDelimiter = "|" + const priorityDelimiter = "=" + h := allocHost() + h.PublicId = agg.PublicId + h.CatalogId = agg.CatalogId + h.ExternalId = agg.ExternalId + h.PluginId = agg.PluginId + h.Name = agg.Name + h.Description = agg.Description + h.CreateTime = agg.CreateTime + h.UpdateTime = agg.UpdateTime + + // This function is used to protect against someone messing with the order + // in the DB by doing some validation + prioritySortFunc := func(in []string) error { + var sortErr error + sort.Slice(in, func(i, j int) bool { + ini := strings.Split(in[i], priorityDelimiter) + if len(ini) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[i])) + return false + } + inj := strings.Split(in[j], priorityDelimiter) + if len(inj) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[j])) + return false + } + indexi, err := strconv.Atoi(ini[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + indexj, err := strconv.Atoi(inj[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + return indexi < indexj + }) + return sortErr + } + + if agg.IpAddresses != "" { + ips := strings.Split(agg.IpAddresses, aggregateDelimiter) + if len(ips) > 0 { + if err := prioritySortFunc(ips); err != nil { + return nil, err + } + for i, ip := range ips { + // At this point they're in the correct order, but we still + // have to strip off the priority + ips[i] = strings.Split(ip, priorityDelimiter)[1] + } + h.IpAddresses = ips + } + } + + if agg.DnsNames != "" { + names := strings.Split(agg.DnsNames, aggregateDelimiter) + if len(names) > 0 { + if err := prioritySortFunc(names); err != nil { + return nil, err + } + for i, name := range names { + // At this point they're in the correct order, but we still + // have to strip off the priority + names[i] = strings.Split(name, priorityDelimiter)[1] + } + h.DnsNames = names + } + } + + return h, nil +} + +// TableName returns the table name for gorm +func (agg *hostAgg) TableName() string { + return "host_plugin_host_with_value_obj" +} + +func (agg *hostAgg) GetPublicId() string { + return agg.PublicId } diff --git a/internal/host/plugin/host_address_test.go b/internal/host/plugin/host_address_test.go new file mode 100644 index 000000000..e4dc1079f --- /dev/null +++ b/internal/host/plugin/host_address_test.go @@ -0,0 +1,548 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/store" + "github.com/hashicorp/boundary/internal/iam" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHostDnsName_Create(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplugin.TestPlugin(t, conn, "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + host1 := testHost(t, conn, cat.GetPublicId(), "external") + + type args struct { + hostId string + name string + priority uint32 + } + + tests := []struct { + name string + args args + want *host.DnsName + wantNewErr bool + skipNewFunc bool + wantDbErr bool + }{ + { + name: "blank-host-id-validate", + args: args{ + hostId: "", + name: "foo.bar.com", + priority: 1, + }, + wantNewErr: true, + }, + { + name: "blank-name-validate", + args: args{ + hostId: host1.GetPublicId(), + name: "", + priority: 1, + }, + wantNewErr: true, + }, + { + name: "blank-priority-validate", + args: args{ + hostId: host1.GetPublicId(), + name: "foo.bar.com", + }, + wantNewErr: true, + }, + { + name: "blank-host-id-db", + args: args{ + name: "foo.bar.com", + priority: 1, + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "blank-name-db", + args: args{ + hostId: host1.GetPublicId(), + priority: 1, + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "blank-priority-db", + args: args{ + hostId: host1.GetPublicId(), + name: "foo.bar.com", + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "valid", + args: args{ + hostId: host1.GetPublicId(), + name: "foo.bar.com", + priority: 1, + }, + want: &host.DnsName{ + DnsName: &store.DnsName{ + HostId: host1.GetPublicId(), + Name: "foo.bar.com", + Priority: 1, + }, + }, + }, + { + name: "duplicate-name", + args: args{ + hostId: host1.GetPublicId(), + name: "foo.bar.com", + priority: 2, + }, + want: &host.DnsName{ + DnsName: &store.DnsName{ + HostId: host1.GetPublicId(), + Name: "foo.bar.com", + Priority: 2, + }, + }, + wantDbErr: true, + }, + { + name: "duplicate-priority", + args: args{ + hostId: host1.GetPublicId(), + name: "baz.bar.com", + priority: 1, + }, + want: &host.DnsName{ + DnsName: &store.DnsName{ + HostId: host1.GetPublicId(), + Name: "baz.bar.com", + Priority: 1, + }, + }, + wantDbErr: true, + }, + { + name: "valid-second", + args: args{ + hostId: host1.GetPublicId(), + name: "baz.bar.com", + priority: 2, + }, + want: &host.DnsName{ + DnsName: &store.DnsName{ + HostId: host1.GetPublicId(), + Name: "baz.bar.com", + Priority: 2, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + var got *host.DnsName + var err error + if !tt.skipNewFunc { + got, err = host.NewDnsName(ctx, tt.args.hostId, tt.args.priority, tt.args.name) + if tt.wantNewErr { + require.Error(err) + return + } + require.NoError(err) + require.Equal(tt.want, got) + } else { + got = &host.DnsName{ + DnsName: &store.DnsName{ + HostId: tt.args.hostId, + Name: tt.args.name, + Priority: tt.args.priority, + }, + } + } + + require.NotNil(got) + err = w.Create(ctx, got) + if tt.wantDbErr { + require.Error(err) + return + } + require.NoError(err) + }) + } +} + +func TestHostIpAddress_Create(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplugin.TestPlugin(t, conn, "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + host1 := testHost(t, conn, cat.GetPublicId(), "external") + + type args struct { + hostId string + address string + priority uint32 + } + + tests := []struct { + name string + args args + want *host.IpAddress + wantNewErr bool + skipNewFunc bool + wantDbErr bool + }{ + { + name: "blank-host-id-validate", + args: args{ + hostId: "", + address: "1.2.3.4", + priority: 1, + }, + wantNewErr: true, + }, + { + name: "blank-name-validate", + args: args{ + hostId: host1.GetPublicId(), + address: "", + priority: 1, + }, + wantNewErr: true, + }, + { + name: "blank-priority-validate", + args: args{ + hostId: host1.GetPublicId(), + address: "1.2.3.4", + }, + wantNewErr: true, + }, + { + name: "bad-address-validate", + args: args{ + hostId: host1.GetPublicId(), + address: "foo.bar.com", + }, + wantNewErr: true, + }, + { + name: "blank-host-id-db", + args: args{ + address: "1.2.3.4", + priority: 1, + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "blank-address-db", + args: args{ + hostId: host1.GetPublicId(), + priority: 1, + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "blank-priority-db", + args: args{ + hostId: host1.GetPublicId(), + address: "1.2.3.4", + }, + skipNewFunc: true, + wantDbErr: true, + }, + { + name: "valid", + args: args{ + hostId: host1.GetPublicId(), + address: "1.2.3.4", + priority: 1, + }, + want: &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: host1.GetPublicId(), + Address: "1.2.3.4", + Priority: 1, + }, + }, + }, + { + name: "duplicate-name", + args: args{ + hostId: host1.GetPublicId(), + address: "1.2.3.4", + priority: 2, + }, + want: &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: host1.GetPublicId(), + Address: "1.2.3.4", + Priority: 2, + }, + }, + wantDbErr: true, + }, + { + name: "duplicate-priority", + args: args{ + hostId: host1.GetPublicId(), + address: "2.3.4.5", + priority: 1, + }, + want: &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: host1.GetPublicId(), + Address: "2.3.4.5", + Priority: 1, + }, + }, + wantDbErr: true, + }, + { + name: "valid-second", + args: args{ + hostId: host1.GetPublicId(), + address: "2.3.4.5", + priority: 2, + }, + want: &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: host1.GetPublicId(), + Address: "2.3.4.5", + Priority: 2, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + var got *host.IpAddress + var err error + if !tt.skipNewFunc { + got, err = host.NewIpAddress(ctx, tt.args.hostId, tt.args.priority, tt.args.address) + if tt.wantNewErr { + require.Error(err) + return + } + require.NoError(err) + require.Equal(tt.want, got) + } else { + got = &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: tt.args.hostId, + Address: tt.args.address, + Priority: tt.args.priority, + }, + } + } + + require.NotNil(got) + err = w.Create(ctx, got) + if tt.wantDbErr { + require.Error(err) + return + } + require.NoError(err) + }) + } +} + +func TestHostDnsName_Delete(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplugin.TestPlugin(t, conn, "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + host1 := testHost(t, conn, cat.GetPublicId(), "external") + addr1, err := host.NewDnsName(ctx, host1.GetPublicId(), 1, "addr1.foo.com") + require.NoError(t, err) + require.NoError(t, w.Create(ctx, addr1)) + + type args struct { + hostId string + name string + priority uint32 + } + + tests := []struct { + name string + args args + wantDelete bool + wantError bool + }{ + { + name: "wrong_host_id", + args: args{ + hostId: "something", + name: addr1.GetName(), + priority: 1, + }, + }, + { + name: "missing_priority", + args: args{ + hostId: addr1.GetHostId(), + name: addr1.GetName(), + }, + wantError: true, + }, + { + name: "wrong_priority", + args: args{ + hostId: addr1.GetHostId(), + name: addr1.GetName(), + priority: 2, + }, + }, + { + name: "valid", + args: args{ + hostId: addr1.GetHostId(), + name: addr1.GetName(), + priority: 1, + }, + wantDelete: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got := &host.DnsName{ + DnsName: &store.DnsName{ + HostId: tt.args.hostId, + Name: tt.args.name, + Priority: tt.args.priority, + }, + } + require.NoError(t, err) + require.NotNil(t, got) + k, err := w.Delete(ctx, got) + if tt.wantError { + assert.Error(t, err) + return + } + require.NoError(t, err) + if tt.wantDelete { + assert.Equal(t, 1, k) + } else { + assert.Equal(t, 0, k) + } + }) + } +} + +func TestHostIpAddress_Delete(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + w := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplugin.TestPlugin(t, conn, "test") + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + host1 := testHost(t, conn, cat.GetPublicId(), "external") + addr1, err := host.NewIpAddress(ctx, host1.GetPublicId(), 1, "1.2.3.4") + require.NoError(t, err) + require.NoError(t, w.Create(ctx, addr1)) + + type args struct { + hostId string + address string + priority uint32 + } + + tests := []struct { + name string + args args + wantDelete bool + wantError bool + }{ + { + name: "wrong_host_id", + args: args{ + hostId: "something", + address: addr1.GetAddress(), + priority: 1, + }, + }, + { + name: "missing_priority", + args: args{ + hostId: addr1.GetHostId(), + address: addr1.GetAddress(), + }, + wantError: true, + }, + { + name: "wrong_priority", + args: args{ + hostId: addr1.GetHostId(), + address: addr1.GetAddress(), + priority: 2, + }, + }, + { + name: "valid", + args: args{ + hostId: addr1.GetHostId(), + address: addr1.GetAddress(), + priority: 1, + }, + wantDelete: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got := &host.IpAddress{ + IpAddress: &store.IpAddress{ + HostId: tt.args.hostId, + Address: tt.args.address, + Priority: tt.args.priority, + }, + } + require.NoError(t, err) + require.NotNil(t, got) + k, err := w.Delete(ctx, got) + if tt.wantError { + assert.Error(t, err) + return + } + require.NoError(t, err) + if tt.wantDelete { + assert.Equal(t, 1, k) + } else { + assert.Equal(t, 0, k) + } + }) + } +} diff --git a/internal/host/plugin/host_test.go b/internal/host/plugin/host_test.go index ee7bd501c..b82a5c3f8 100644 --- a/internal/host/plugin/host_test.go +++ b/internal/host/plugin/host_test.go @@ -21,9 +21,9 @@ func TestHost_Create(t *testing.T) { cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) type args struct { - catalogId string - address string - opts []Option + catalogId string + externalId string + opts []Option } tests := []struct { @@ -35,16 +35,16 @@ func TestHost_Create(t *testing.T) { { name: "blank-catalogId", args: args{ - catalogId: "", - address: "foo.bar.com", + catalogId: "", + externalId: "external_id", }, want: &Host{Host: &store.Host{ - Address: "foo.bar.com", + ExternalId: "external_id", }}, wantErr: true, }, { - name: "blank-address", + name: "blank-external-id", args: args{ catalogId: cat.GetPublicId(), }, @@ -56,47 +56,47 @@ func TestHost_Create(t *testing.T) { { name: "valid-no-options", args: args{ - catalogId: cat.GetPublicId(), - address: "foo.bar.com", + catalogId: cat.GetPublicId(), + externalId: "valid-no-options", }, want: &Host{ Host: &store.Host{ - CatalogId: cat.GetPublicId(), - Address: "foo.bar.com", + CatalogId: cat.GetPublicId(), + ExternalId: "valid-no-options", }, }, }, { name: "valid-with-name", args: args{ - catalogId: cat.GetPublicId(), - address: "foo.bar.com", + catalogId: cat.GetPublicId(), + externalId: "valid-with-name", opts: []Option{ WithName("test-name"), }, }, want: &Host{ Host: &store.Host{ - CatalogId: cat.GetPublicId(), - Address: "foo.bar.com", - Name: "test-name", + CatalogId: cat.GetPublicId(), + ExternalId: "valid-with-name", + Name: "test-name", }, }, }, { name: "duplicate-name", args: args{ - catalogId: cat.GetPublicId(), - address: "foo.bar.com", + catalogId: cat.GetPublicId(), + externalId: "duplicate-name", opts: []Option{ WithName("test-name"), }, }, want: &Host{ Host: &store.Host{ - CatalogId: cat.GetPublicId(), - Address: "foo.bar.com", - Name: "test-name", + CatalogId: cat.GetPublicId(), + ExternalId: "duplicate-name", + Name: "test-name", }, }, wantErr: true, @@ -104,25 +104,25 @@ func TestHost_Create(t *testing.T) { { name: "valid-duplicate-name-different-catalog", args: args{ - catalogId: cat2.GetPublicId(), - address: "foo.bar.com", + catalogId: cat2.GetPublicId(), + externalId: "valid-duplicate-name-different-catalog", opts: []Option{ WithName("test-name"), }, }, want: &Host{ Host: &store.Host{ - CatalogId: cat2.GetPublicId(), - Address: "foo.bar.com", - Name: "test-name", + CatalogId: cat2.GetPublicId(), + ExternalId: "valid-duplicate-name-different-catalog", + Name: "test-name", }, }, }, { name: "valid-with-description", args: args{ - catalogId: cat.GetPublicId(), - address: "foo.bar.com", + catalogId: cat.GetPublicId(), + externalId: "valid-with-description", opts: []Option{ WithDescription("test-description"), }, @@ -130,7 +130,7 @@ func TestHost_Create(t *testing.T) { want: &Host{ Host: &store.Host{ CatalogId: cat.GetPublicId(), - Address: "foo.bar.com", + ExternalId: "valid-with-description", Description: "test-description", }, }, @@ -141,7 +141,7 @@ func TestHost_Create(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - got := newHost(ctx, tt.args.catalogId, tt.args.address, tt.args.opts...) + got := newHost(ctx, tt.args.catalogId, tt.args.externalId, tt.args.opts...) require.NotNil(t, got) assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) @@ -163,6 +163,9 @@ func TestHost_Create(t *testing.T) { } } +// TODO: Test Deletion directly of host, of set membership, and of cascading +// from the set + func TestHost_SetTableName(t *testing.T) { defaultTableName := "host_plugin_host" tests := []struct { @@ -200,3 +203,15 @@ func TestHost_SetTableName(t *testing.T) { }) } } + +func testHost(t *testing.T, conn *db.DB, catId, externId string) *Host { + t.Helper() + w := db.New(conn) + ctx := context.Background() + host1 := newHost(ctx, catId, externId) + var err error + host1.PublicId, err = newHostId(ctx, catId, externId) + require.NoError(t, err) + require.NoError(t, w.Create(ctx, host1)) + return host1 +} \ No newline at end of file diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index b711478cb..0467bfa82 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -16,11 +16,15 @@ type Option func(*options) // options = how options are represented type options struct { + withPluginId string withName string withDescription string withAttributes *structpb.Struct withSecrets *structpb.Struct withPreferredEndpoints []string + withIpAddresses []string + withDnsNames []string + withLimit int } func getDefaultOptions() options { @@ -29,6 +33,13 @@ func getDefaultOptions() options { } } +// WithPluginId provides an optional plugin id. +func withPluginId(with string) Option { + return func(o *options) { + o.withPluginId = with + } +} + // WithDescription provides an optional description. func WithDescription(desc string) Option { return func(o *options) { @@ -63,3 +74,26 @@ func WithPreferredEndpoints(with []string) Option { o.withPreferredEndpoints = with } } + +// withIpAddresses provides an optional list of ip addresses. +func withIpAddresses(with []string) Option { + return func(o *options) { + o.withIpAddresses = with + } +} + +// withDnsNames provides an optional list of dns names. +func withDnsNames(with []string) Option { + return func(o *options) { + o.withDnsNames = with + } +} + +// WithLimit provides an option to provide a limit. Intentionally allowing +// negative integers. If WithLimit < 0, then unlimited results are +// returned. If WithLimit == 0, then default limits are used for results. +func WithLimit(l int) Option { + return func(o *options) { + o.withLimit = l + } +} diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index aab698c2a..758e3d619 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -14,16 +14,40 @@ func Test_GetOpts(t *testing.T) { testOpts.withName = "test" assert.Equal(t, opts, testOpts) }) + t.Run("WithPluginId", func(t *testing.T) { + opts := getOpts(withPluginId("test")) + testOpts := getDefaultOptions() + testOpts.withPluginId = "test" + assert.Equal(t, opts, testOpts) + }) t.Run("WithDescription", func(t *testing.T) { opts := getOpts(WithDescription("test desc")) testOpts := getDefaultOptions() testOpts.withDescription = "test desc" assert.Equal(t, opts, testOpts) }) + t.Run("WithLimit", func(t *testing.T) { + opts := getOpts(WithLimit(5)) + testOpts := getDefaultOptions() + testOpts.withLimit = 5 + assert.Equal(t, opts, testOpts) + }) t.Run("WithPreferredEndpoints", func(t *testing.T) { opts := getOpts(WithPreferredEndpoints([]string{"foo"})) testOpts := getDefaultOptions() testOpts.withPreferredEndpoints = []string{"foo"} assert.EqualValues(t, opts, testOpts) }) + t.Run("withDnsNames", func(t *testing.T) { + opts := getOpts(withDnsNames([]string{"foo"})) + testOpts := getDefaultOptions() + testOpts.withDnsNames = []string{"foo"} + assert.EqualValues(t, opts, testOpts) + }) + t.Run("withIpAddresses", func(t *testing.T) { + opts := getOpts(withIpAddresses([]string{"foo"})) + testOpts := getDefaultOptions() + testOpts.withIpAddresses = []string{"foo"} + assert.EqualValues(t, opts, testOpts) + }) } diff --git a/internal/host/plugin/repository_host.go b/internal/host/plugin/repository_host.go new file mode 100644 index 000000000..2979d87b4 --- /dev/null +++ b/internal/host/plugin/repository_host.go @@ -0,0 +1,284 @@ +package plugin + +import ( + "context" + "fmt" + "sort" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" +) + +// UpsertHost inserts phs into the repository or updates its current +// attributes/set memberships and returns Hosts. h is not changed. hc must +// contain a valid public ID and scope ID. Each ph in phs must not contain a +// PublicId but must contain an external ID. The PublicId is generated and +// assigned by this method. +// +// NOTE: If phs is empty, this assumes that there are simply no hosts that +// matched the given sets! Which means it will remove all hosts from the given +// sets. +func (r *Repository) UpsertHosts( + ctx context.Context, + hc *HostCatalog, + sets []string, + phs []*plgpb.ListHostsResponseHost, + _ ...Option) ([]*Host, error) { + const op = "plugin.(Repository).UpsertHosts" + if phs == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil plugin hosts") + } + for _, ph := range phs { + if ph.GetExternalId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host external id") + } + } + if hc == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil host catalog") + } + if hc.GetPublicId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + } + if hc.GetScopeId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if sets == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil sets") + } + if len(sets) == 0 { // At least one must have been given to the plugin + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty sets") + } + + // hostInfo stores the info we need for the transaction below as well as + // which sets they matched + type hostInfo struct { + h *Host + ips []interface{} + dnsNames []interface{} + sets map[string]struct{} + } + hostMapping := make(map[string]hostInfo, len(phs)) + var err error + var totalMsgs uint32 + + for _, ph := range phs { + totalMsgs += 2 // delete and create + + h := newHost(ctx, + hc.GetPublicId(), + ph.GetExternalId(), + withIpAddresses(ph.GetIpAddresses()), + withDnsNames(ph.GetDnsNames()), + withPluginId(hc.GetPluginId())) + h.PublicId, err = newHostId(ctx, hc.GetPublicId(), ph.GetExternalId()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + var ipAddresses []interface{} + if len(h.GetIpAddresses()) > 0 { + sort.Strings(h.IpAddresses) + ipAddresses = make([]interface{}, 0, len(h.GetIpAddresses())) + for i, a := range h.GetIpAddresses() { + obj, err := host.NewIpAddress(ctx, h.PublicId, uint32(i+1), a) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + ipAddresses = append(ipAddresses, obj) + } + } + + var dnsNames []interface{} + if len(h.GetDnsNames()) > 0 { + sort.Strings(h.DnsNames) + dnsNames = make([]interface{}, 0, len(h.GetDnsNames())) + for i, n := range h.GetDnsNames() { + obj, err := host.NewDnsName(ctx, h.PublicId, uint32(i+1), n) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + dnsNames = append(dnsNames, obj) + } + } + + hi := hostInfo{ + h: h, + ips: ipAddresses, + dnsNames: dnsNames, + } + + for _, id := range ph.GetSetIds() { + if hi.sets == nil { + hi.sets = make(map[string]struct{}) + } + hi.sets[id] = struct{}{} + } + + hostMapping[h.PublicId] = hi + + totalMsgs += uint32(len(ipAddresses) + len(dnsNames)) + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, hc.GetScopeId(), kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var returnedHosts []*Host + + // Here's what this function does: first it deletes a host, which causes + // cascading deletes on ip addresses and dns names (but all within the DB). + // Then the host is recreated, which uses the same public ID (since it's + // based on the host catalog/external ID), along with new/updated + // information. + // + // If we ever do start allowing hosts directly in targets this may cause a + // problem because the host deletion would probably cascade to the target + // and remove it from the target host sources, but at this point there are + // several issues with that scenario so it's not soon... + // + // The main reason I'm doing it this way is because I'm not sure how to do + // oplogs with custom queries. Otherwise a custom insert/on conflict query + // could replace the initial deletion. @jimlambrt @mgaffney help? :-D + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + // FIXME: We need to ensure hosts no longer found have their + // memberships removed. To do this, first list all set memberships. + // This is waiting on membership to be plumbed through to gorm, + // which in turn is waiting on figuring out whether or not we can + // share that between static and plugin. + // + // Next, for each set, check whether a given host ID is in the + // hostMapping and if so, if the set ID is in the host's sets. If + // not, queue that membership entry for deletion. + msgs := make([]*oplog.Message, 0, totalMsgs+3) + ticket, err := w.GetTicket(hc) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + for _, hi := range hostMapping { + ret := hi.h.clone() + + var hOplogMsg oplog.Message + // We don't need to check whether something was deleted because + // it's okay if it fails + if _, err := w.Delete(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &hOplogMsg) + + hOplogMsg = oplog.Message{} + if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &hOplogMsg) + + if len(hi.ips) > 0 { + ipOplogMsgs := make([]*oplog.Message, 0, len(hi.ips)) + if err := w.CreateItems(ctx, hi.ips, db.NewOplogMsgs(&ipOplogMsgs)); err != nil { + return err + } + msgs = append(msgs, ipOplogMsgs...) + } + + if len(hi.dnsNames) > 0 { + dnsOplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNames)) + if err := w.CreateItems(ctx, hi.dnsNames, db.NewOplogMsgs(&dnsOplogMsgs)); err != nil { + return err + } + msgs = append(msgs, dnsOplogMsgs...) + } + + // FIXME: Add set memberships here + + returnedHosts = append(returnedHosts, ret) + } + + metadata := hc.oplog(oplog.OpType_OP_TYPE_CREATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + return nil + }, + ) + + if err != nil { + if errors.IsCheckConstraintError(err) || errors.IsNotNullError(err) { + return nil, errors.New(ctx, + errors.InvalidAddress, + op, + fmt.Sprintf("in catalog: %s", hc.GetPublicId()), + errors.WithWrap(err), + ) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", hc.PublicId))) + } + return returnedHosts, nil +} + +// LookupHost will look up a host in the repository. If the host is not +// found, it will return nil, nil. All options are ignored. +func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, error) { + const op = "plugin.(Repository).LookupHost" + if publicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + ha := &hostAgg{ + PublicId: publicId, + } + if err := r.reader.LookupByPublicId(ctx, ha); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) + } + h, err := ha.toHost(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed to convert host agg for %s", publicId))) + } + return h, nil +} + +// ListHosts returns a slice of Hosts for the catalogId. +// WithLimit is the only option supported. +func (r *Repository) ListHosts(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) { + const op = "plugin.(Repository).ListHosts" + if catalogId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var hostAggs []*hostAgg + err := r.reader.SearchWhere(ctx, &hostAggs, "catalog_id = ?", []interface{}{catalogId}, db.WithLimit(limit)) + + switch { + case err != nil: + return nil, errors.Wrap(ctx, err, op) + case hostAggs == nil: + return nil, nil + } + + hosts := make([]*Host, 0, len(hostAggs)) + for _, ha := range hostAggs { + host, err := ha.toHost(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hosts = append(hosts, host) + } + + return hosts, nil +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 63d6f6e44..06fecbeb3 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -87,11 +87,6 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } } - oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) - if err != nil { - return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) - } - var preferredEndpoints []interface{} if s.PreferredEndpoints != nil { preferredEndpoints = make([]interface{}, 0, len(s.PreferredEndpoints)) @@ -104,6 +99,11 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } } + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + var returnedHostSet *HostSet _, err = r.writer.DoTx( ctx, @@ -185,6 +185,7 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host setToReturn := sets[0] var hostIdsToReturn []string + // FIXME: change to use the database if plg != nil && opts.WithSetMembers { cat, err := r.getCatalog(ctx, setToReturn.GetCatalogId()) if err != nil { diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 9d177a001..cffa56bc9 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -489,16 +489,15 @@ func TestRepository_Endpoints(t *testing.T) { }) assert.Empty(cmp.Diff(got, tt.want, protocmp.Transform())) - // TODO: Remove this once we no longer persist all host lookup calls - // when retrieving the endpoints. for _, ep := range got { h := allocHost() h.PublicId = ep.HostId require.NoError(rw.LookupByPublicId(ctx, h)) - assert.Equal(uint32(1), h.Version) assert.Equal(ep.HostId, h.PublicId) - assert.Equal(ep.Address, h.Address) + // TODO: Uncomment when we have a better way to lookup host + // with it's address + // assert.Equal(ep.Address, h.Address) assert.Equal(catalog.GetPublicId(), h.GetCatalogId()) } }) diff --git a/internal/host/plugin/repository_host_test.go b/internal/host/plugin/repository_host_test.go new file mode 100644 index 000000000..de59125a9 --- /dev/null +++ b/internal/host/plugin/repository_host_test.go @@ -0,0 +1,245 @@ +package plugin + +import ( + "context" + "fmt" + "sort" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_UpsertHosts(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + + plg := hostplg.TestPlugin(t, conn, "create") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), + } + + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + const setCount int = 3 + setIds := make([]string, 0, setCount) + for i := 0; i < setCount; i++ { + set := TestSet(t, conn, kms, catalog, plgm) + setIds = append(setIds, set.GetPublicId()) + } + phs, exp := TestExternalHosts(t, catalog, setIds, setCount) + + type input struct { + catalog *HostCatalog + sets []string + phs []*plgpb.ListHostsResponseHost + exp []*Host + } + + tests := []struct { + name string + in func() *input + opts []Option + wantIsErr errors.Code + }{ + { + name: "nil-hosts", + in: func() *input { + return &input{ + catalog: catalog, + sets: setIds, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-external-id-hosts", + in: func() *input { + testPhs, _ := TestExternalHosts(t, catalog, setIds, setCount) + testPhs[1].ExternalId = "" + return &input{ + catalog: catalog, + sets: setIds, + phs: testPhs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-catalog", + in: func() *input { + return &input{ + sets: setIds, + phs: phs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-catalog-id", + in: func() *input { + cat := catalog.clone() + cat.PublicId = "" + return &input{ + catalog: cat, + sets: setIds, + phs: phs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-scope-id", + in: func() *input { + cat := catalog.clone() + cat.ScopeId = "" + return &input{ + catalog: cat, + sets: setIds, + phs: phs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-sets", + in: func() *input { + return &input{ + catalog: catalog, + phs: phs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "no-sets", + in: func() *input { + return &input{ + catalog: catalog, + sets: make([]string, 0), + phs: phs, + } + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "valid", + in: func() *input { + return &input{ + catalog: catalog, + sets: setIds, + phs: phs, + exp: exp, + } + }, + }, + { + name: "valid-changed-values", + in: func() *input { + ph := phs[1] + e := exp[1] + newIp := testGetIpAddress(t) + newName := testGetDnsName(t) + ph.IpAddresses = append(ph.IpAddresses, newIp) + e.IpAddresses = append(e.IpAddresses, newIp) + ph.DnsNames = append(ph.DnsNames, newName) + e.DnsNames = append(e.DnsNames, newName) + // These are sorted by the repo function, so we need to match + sort.Strings(e.IpAddresses) + sort.Strings(e.DnsNames) + + ph.SetIds = ph.SetIds[0 : len(ph.SetIds)-1] + return &input{ + catalog: catalog, + sets: setIds, + phs: phs, + exp: exp, + } + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms, plgm) + require.NoError(err) + require.NotNil(repo) + in := tt.in() + got, err := repo.UpsertHosts(ctx, in.catalog, in.sets, in.phs, tt.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + require.NoError(err, fmt.Sprintf("%v", in.catalog)) + require.NotNil(got) + + // Basic tests + assert.Len(got, len(in.phs)) + assert.NoError(db.TestVerifyOplog(t, rw, in.catalog.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + + // Make sure outputs match. Ignore timestamps. + assert.Empty( + cmp.Diff( + in.exp, + got, + cmpopts.IgnoreUnexported(Host{}, store.Host{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + cmpopts.SortSlices(func(x, y *Host) bool { + return x.GetPublicId() < y.GetPublicId() + }), + ), + ) + + // Check again, but via performing an explicit list + got, err = repo.ListHosts(ctx, in.catalog.GetPublicId()) + require.NoError(err) + require.NotNil(got) + assert.Len(got, len(in.phs)) + assert.Empty( + cmp.Diff( + in.exp, + got, + cmpopts.IgnoreUnexported(Host{}, store.Host{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + cmpopts.SortSlices(func(x, y *Host) bool { + return x.GetPublicId() < y.GetPublicId() + }), + ), + ) + + // Now individually call read on each host + for _, exp := range in.exp { + got, err := repo.LookupHost(ctx, exp.GetPublicId()) + require.NoError(err) + require.NotNil(got) + assert.Empty( + cmp.Diff( + exp, + got, + cmpopts.IgnoreUnexported(Host{}, store.Host{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + ), + ) + } + }) + } +} diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 1f2d7dcec..b0771bf35 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -155,127 +155,6 @@ func (x *HostCatalog) GetAttributes() []byte { return nil } -type Host struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // public_id is a surrogate key suitable for use in a public API. - // @inject_tag: `gorm:"primary_key"` - PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` - // The update_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` - // name is optional. If set, it must be unique within - // catalog_id. - // @inject_tag: `gorm:"default:null"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` - // description is optional. - // @inject_tag: `gorm:"default:null"` - Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` - // catalog_id is the public_id of the owning - // plugin_host_catalog and must be set. - // @inject_tag: `gorm:"not_null"` - CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` - // version allows optimistic locking of the resource - // @inject_tag: `gorm:"default:null"` - Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` - // address is the IP Address or DNS name of the host. - // @inject_tag: `gorm:"default:null"` - Address string `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` -} - -func (x *Host) Reset() { - *x = Host{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Host) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Host) ProtoMessage() {} - -func (x *Host) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_host_plugin_store_v1_host_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 Host.ProtoReflect.Descriptor instead. -func (*Host) Descriptor() ([]byte, []int) { - return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1} -} - -func (x *Host) GetPublicId() string { - if x != nil { - return x.PublicId - } - return "" -} - -func (x *Host) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *Host) GetUpdateTime() *timestamp.Timestamp { - if x != nil { - return x.UpdateTime - } - return nil -} - -func (x *Host) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Host) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *Host) GetCatalogId() string { - if x != nil { - return x.CatalogId - } - return "" -} - -func (x *Host) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *Host) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - type HostSet struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -315,7 +194,7 @@ type HostSet struct { func (x *HostSet) Reset() { *x = HostSet{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -328,7 +207,7 @@ func (x *HostSet) String() string { func (*HostSet) ProtoMessage() {} func (x *HostSet) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -341,7 +220,7 @@ func (x *HostSet) ProtoReflect() protoreflect.Message { // Deprecated: Use HostSet.ProtoReflect.Descriptor instead. func (*HostSet) Descriptor() ([]byte, []int) { - return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2} + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1} } func (x *HostSet) GetPublicId() string { @@ -437,7 +316,7 @@ type HostCatalogSecret struct { func (x *HostCatalogSecret) Reset() { *x = HostCatalogSecret{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -450,7 +329,7 @@ func (x *HostCatalogSecret) String() string { func (*HostCatalogSecret) ProtoMessage() {} func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3] + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -463,7 +342,7 @@ func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message { // Deprecated: Use HostCatalogSecret.ProtoReflect.Descriptor instead. func (*HostCatalogSecret) Descriptor() ([]byte, []int) { - return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3} + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2} } func (x *HostCatalogSecret) GetCatalogId() string { @@ -508,6 +387,216 @@ func (x *HostCatalogSecret) GetKeyId() string { return "" } +// TODO: Add a field which tracks if the host in cache should be considered +// invalid and fall back to the plugin provided data. +type Host struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // external_id is an id provided by the plugin. + // @inject_tag: `gorm:"not_null"` + ExternalId string `protobuf:"bytes,2,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty" gorm:"not_null"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,4,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + CatalogId string `protobuf:"bytes,7,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` + // @inject_tag: `gorm:"-"` + Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"-"` + // ip_addresses are the ip addresses associated with this host and will + // be persisted in the db through the HostAddress message. + // @inject_tag: `gorm:"-"` + IpAddresses []string `protobuf:"bytes,9,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty" gorm:"-"` + // dns_names are the dns names associated with this host and will + // be persisted in the db through the HostAddress message. + // @inject_tag: `gorm:"-"` + DnsNames []string `protobuf:"bytes,10,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty" gorm:"-"` +} + +func (x *Host) Reset() { + *x = Host{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Host) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Host) ProtoMessage() {} + +func (x *Host) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 Host.ProtoReflect.Descriptor instead. +func (*Host) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3} +} + +func (x *Host) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Host) GetExternalId() string { + if x != nil { + return x.ExternalId + } + return "" +} + +func (x *Host) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Host) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Host) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Host) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Host) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + +func (x *Host) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Host) GetIpAddresses() []string { + if x != nil { + return x.IpAddresses + } + return nil +} + +func (x *Host) GetDnsNames() []string { + if x != nil { + return x.DnsNames + } + return nil +} + +type HostSetMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: `gorm:"primary_key"` + HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"primary_key"` + SetId string `protobuf:"bytes,2,opt,name=set_id,json=setId,proto3" json:"set_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"default:null"` + CatalogId string `protobuf:"bytes,3,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"default:null"` +} + +func (x *HostSetMember) Reset() { + *x = HostSetMember{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostSetMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostSetMember) ProtoMessage() {} + +func (x *HostSetMember) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_plugin_store_v1_host_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 HostSetMember.ProtoReflect.Descriptor instead. +func (*HostSetMember) Descriptor() ([]byte, []int) { + return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{4} +} + +func (x *HostSetMember) GetHostId() string { + if x != nil { + return x.HostId + } + return "" +} + +func (x *HostSetMember) GetSetId() string { + if x != nil { + return x.SetId + } + return "" +} + +func (x *HostSetMember) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + var File_controller_storage_host_plugin_store_v1_host_proto protoreflect.FileDescriptor var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ @@ -550,77 +639,88 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x04, 0x48, 0x6f, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, - 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, - 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, - 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, - 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, - 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, + 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, + 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x04, 0x48, 0x6f, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, + 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, + 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73, + 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -635,23 +735,24 @@ func file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP() []byt return file_controller_storage_host_plugin_store_v1_host_proto_rawDescData } -var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{ (*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog - (*Host)(nil), // 1: controller.storage.host.plugin.store.v1.Host - (*HostSet)(nil), // 2: controller.storage.host.plugin.store.v1.HostSet - (*HostCatalogSecret)(nil), // 3: controller.storage.host.plugin.store.v1.HostCatalogSecret - (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp + (*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet + (*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret + (*Host)(nil), // 3: controller.storage.host.plugin.store.v1.Host + (*HostSetMember)(nil), // 4: controller.storage.host.plugin.store.v1.HostSetMember + (*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{ - 4, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 2: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 3: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 4: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 5: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 6: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 7: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 6: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 7: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name @@ -678,18 +779,6 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { } } file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Host); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HostSet); i { case 0: return &v.state @@ -701,7 +790,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { return nil } } - file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HostCatalogSecret); i { case 0: return &v.state @@ -713,6 +802,30 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { return nil } } + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Host); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostSetMember); 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{ @@ -720,7 +833,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_plugin_store_v1_host_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index a4159e724..45d38c42e 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -2,12 +2,18 @@ package plugin import ( "context" + "crypto/rand" + "fmt" + "io" + "net" "testing" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/hashicorp/go-secure-stdlib/base62" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -67,6 +73,65 @@ func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm return set } +func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, count int) ([]*plgpb.ListHostsResponseHost, []*Host) { + t.Helper() + require := require.New(t) + retRH := make([]*plgpb.ListHostsResponseHost, 0, count) + retH := make([]*Host, 0, count) + + for i := 0; i < count; i++ { + externalId, err := base62.Random(10) + require.NoError(err) + + ipStr := testGetIpAddress(t) + dnsName := testGetDnsName(t) + + retRH = append(retRH, &plgpb.ListHostsResponseHost{ + ExternalId: externalId, + SetIds: setIds, + IpAddresses: []string{ipStr}, + DnsNames: []string{dnsName}, + }) + + publicId, err := newHostId(context.Background(), catalog.PublicId, externalId) + require.NoError(err) + + retH = append(retH, &Host{ + PluginId: catalog.PluginId, + Host: &store.Host{ + CatalogId: catalog.PublicId, + PublicId: publicId, + ExternalId: externalId, + IpAddresses: []string{ipStr}, + DnsNames: []string{dnsName}, + }, + }) + } + + return retRH, retH +} + +func testGetDnsName(t *testing.T) string { + dnsName, err := base62.Random(10) + require.NoError(t, err) + return fmt.Sprintf("%s.example.com", dnsName) +} + +func testGetIpAddress(t *testing.T) string { + ipBytes := make([]byte, 4) + for { + lr := io.LimitReader(rand.Reader, 4) + n, err := lr.Read(ipBytes) + require.NoError(t, err) + require.Equal(t, n, 4) + ip := net.IP(ipBytes) + v4 := ip.To4() + if v4 != nil { + return v4.String() + } + } +} + var _ plgpb.HostPluginServiceServer = (*TestPluginServer)(nil) // TestPluginServer provides a host plugin service server where each method can be overwritten for tests. diff --git a/internal/host/static/host.go b/internal/host/static/host.go index b37341f9e..b367111b3 100644 --- a/internal/host/static/host.go +++ b/internal/host/static/host.go @@ -38,6 +38,16 @@ func NewHost(catalogId string, opt ...Option) (*Host, error) { return host, nil } +// For compatibility with the general Host type +func (h *Host) GetIpAddresses() []string { + return nil +} + +// For compatibility with the general Host type +func (h *Host) GetDnsNames() []string { + return nil +} + // TableName returns the table name for the host. func (h *Host) TableName() string { if h.tableName != "" { diff --git a/internal/host/static/repository_host_set_member.go b/internal/host/static/repository_host_set_member.go index dead39500..76b25fb99 100644 --- a/internal/host/static/repository_host_set_member.go +++ b/internal/host/static/repository_host_set_member.go @@ -100,8 +100,11 @@ func updateVersion(ctx context.Context, w db.Writer, wrapper wrapping.Wrapper, m switch { case err != nil: return errors.Wrap(ctx, err, op) + case rowsUpdated == 0: + return errors.New(ctx, errors.RecordNotFound, op, "no matching version for host set found") case rowsUpdated > 1: return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated") + } msgs = append(msgs, setMsg) diff --git a/internal/host/store/host.pb.go b/internal/host/store/host.pb.go index a781d8082..c9c8fcaf8 100644 --- a/internal/host/store/host.pb.go +++ b/internal/host/store/host.pb.go @@ -268,6 +268,138 @@ func (x *PreferredEndpoint) GetCondition() string { return "" } +type IpAddress struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: `gorm:"primary_key"` + HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"primary_key"` + Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"not_null` + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *IpAddress) Reset() { + *x = IpAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IpAddress) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IpAddress) ProtoMessage() {} + +func (x *IpAddress) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_store_v1_host_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 IpAddress.ProtoReflect.Descriptor instead. +func (*IpAddress) Descriptor() ([]byte, []int) { + return file_controller_storage_host_store_v1_host_proto_rawDescGZIP(), []int{4} +} + +func (x *IpAddress) GetHostId() string { + if x != nil { + return x.HostId + } + return "" +} + +func (x *IpAddress) GetPriority() uint32 { + if x != nil { + return x.Priority + } + return 0 +} + +func (x *IpAddress) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type DnsName struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: `gorm:"primary_key"` + HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"primary_key"` + Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"not_null` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *DnsName) Reset() { + *x = DnsName{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DnsName) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DnsName) ProtoMessage() {} + +func (x *DnsName) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[5] + 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 DnsName.ProtoReflect.Descriptor instead. +func (*DnsName) Descriptor() ([]byte, []int) { + return file_controller_storage_host_store_v1_host_proto_rawDescGZIP(), []int{5} +} + +func (x *DnsName) GetHostId() string { + if x != nil { + return x.HostId + } + return "" +} + +func (x *DnsName) GetPriority() uint32 { + if x != nil { + return x.Priority + } + return 0 +} + +func (x *DnsName) GetName() string { + if x != nil { + return x.Name + } + return "" +} + var File_controller_storage_host_store_v1_host_proto protoreflect.FileDescriptor var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{ @@ -295,7 +427,18 @@ var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{ 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x07, 0x44, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, @@ -314,12 +457,14 @@ func file_controller_storage_host_store_v1_host_proto_rawDescGZIP() []byte { return file_controller_storage_host_store_v1_host_proto_rawDescData } -var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_controller_storage_host_store_v1_host_proto_goTypes = []interface{}{ (*Catalog)(nil), // 0: controller.storage.host.store.v1.Catalog (*Host)(nil), // 1: controller.storage.host.store.v1.Host (*Set)(nil), // 2: controller.storage.host.store.v1.Set (*PreferredEndpoint)(nil), // 3: controller.storage.host.store.v1.PreferredEndpoint + (*IpAddress)(nil), // 4: controller.storage.host.store.v1.IpAddress + (*DnsName)(nil), // 5: controller.storage.host.store.v1.DnsName } var file_controller_storage_host_store_v1_host_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -383,6 +528,30 @@ func file_controller_storage_host_store_v1_host_proto_init() { return nil } } + file_controller_storage_host_store_v1_host_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IpAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_host_store_v1_host_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DnsName); 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{ @@ -390,7 +559,7 @@ func file_controller_storage_host_store_v1_host_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_store_v1_host_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 5d9722bea..6b38203b7 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -47,42 +47,6 @@ message HostCatalog { bytes attributes = 9; } -message Host { - // public_id is a surrogate key suitable for use in a public API. - // @inject_tag: `gorm:"primary_key"` - string public_id = 1; - - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp create_time = 2; - - // The update_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp update_time = 3; - - // name is optional. If set, it must be unique within - // catalog_id. - // @inject_tag: `gorm:"default:null"` - string name = 4; - - // description is optional. - // @inject_tag: `gorm:"default:null"` - string description = 5; - - // catalog_id is the public_id of the owning - // plugin_host_catalog and must be set. - // @inject_tag: `gorm:"not_null"` - string catalog_id = 6; - - // version allows optimistic locking of the resource - // @inject_tag: `gorm:"default:null"` - uint32 version = 7; - - // address is the IP Address or DNS name of the host. - // @inject_tag: `gorm:"default:null"` - string address = 8; -} - message HostSet { // public_id is a surrogate key suitable for use in a public API. // @inject_tag: `gorm:"primary_key"` @@ -149,4 +113,62 @@ message HostCatalogSecret { // It must be set. // @inject_tag: `gorm:"not_null"` string key_id = 6; +} + +// TODO: Add a field which tracks if the host in cache should be considered +// invalid and fall back to the plugin provided data. +message Host { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // external_id is an id provided by the plugin. + // @inject_tag: `gorm:"not_null"` + string external_id = 2; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 3; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 4; + + // name is optional. If set, it must be unique within + // catalog_id. + // @inject_tag: `gorm:"default:null"` + string name = 5; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 6; + + // catalog_id is the public_id of the owning + // plugin_host_catalog and must be set. + // @inject_tag: `gorm:"not_null"` + string catalog_id = 7; + + // @inject_tag: `gorm:"-"` + uint32 version = 8; + + // ip_addresses are the ip addresses associated with this host and will + // be persisted in the db through the HostAddress message. + // @inject_tag: `gorm:"-"` + repeated string ip_addresses = 9; + + // dns_names are the dns names associated with this host and will + // be persisted in the db through the HostAddress message. + // @inject_tag: `gorm:"-"` + repeated string dns_names = 10; +} + +message HostSetMember { + // @inject_tag: `gorm:"primary_key"` + string host_id = 1; + + // @inject_tag: `gorm:"primary_key"` + string set_id = 2; + + // @inject_tag: `gorm:"default:null"` + string catalog_id = 3; } \ No newline at end of file diff --git a/internal/proto/local/controller/storage/host/store/v1/host.proto b/internal/proto/local/controller/storage/host/store/v1/host.proto index e6123406c..deb95f14b 100644 --- a/internal/proto/local/controller/storage/host/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/store/v1/host.proto @@ -46,4 +46,26 @@ message PreferredEndpoint { // The string text of the preference condition // @inject_tag: `gorm:"not_null"` string condition = 3; +} + +message IpAddress { + // @inject_tag: `gorm:"primary_key"` + string host_id = 1; + + // @inject_tag: `gorm:"primary_key"` + uint32 priority = 2; + + // @inject_tag: `gorm:"not_null` + string address = 3; +} + +message DnsName { + // @inject_tag: `gorm:"primary_key"` + string host_id = 1; + + // @inject_tag: `gorm:"primary_key"` + uint32 priority = 2; + + // @inject_tag: `gorm:"not_null` + string name = 3; } \ No newline at end of file From 1cf94d0e3edd25f412ffdb618b0e2116a7c343ea Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 21 Oct 2021 13:30:31 -0400 Subject: [PATCH 36/71] Fix merge issues --- .../migrations/oss/postgres/{17 => 18}/01_domain_types.up.sql | 0 .../schema/migrations/oss/postgres/{17 => 18}/02_plugin.up.sql | 0 .../migrations/oss/postgres/{17 => 18}/03_hostplugin.up.sql | 0 .../db/schema/migrations/oss/postgres/{17 => 18}/04_host.up.sql | 0 .../migrations/oss/postgres/{17 => 18}/05_plugin_host.up.sql | 0 .../oss/postgres/{17 => 18}/06_preferred_endpoints.up.sql | 0 .../oss/postgres/{17 => 18}/07_wh_session_dimensions.up.sql | 0 internal/servers/controller/handler.go | 2 ++ .../servers/controller/handlers/targets/target_service_test.go | 2 +- 9 files changed, 3 insertions(+), 1 deletion(-) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/01_domain_types.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/02_plugin.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/03_hostplugin.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/04_host.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/05_plugin_host.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/06_preferred_endpoints.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{17 => 18}/07_wh_session_dimensions.up.sql (100%) diff --git a/internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql b/internal/db/schema/migrations/oss/postgres/18/01_domain_types.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/01_domain_types.up.sql rename to internal/db/schema/migrations/oss/postgres/18/01_domain_types.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/02_plugin.up.sql b/internal/db/schema/migrations/oss/postgres/18/02_plugin.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/02_plugin.up.sql rename to internal/db/schema/migrations/oss/postgres/18/02_plugin.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/03_hostplugin.up.sql b/internal/db/schema/migrations/oss/postgres/18/03_hostplugin.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/03_hostplugin.up.sql rename to internal/db/schema/migrations/oss/postgres/18/03_hostplugin.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/04_host.up.sql b/internal/db/schema/migrations/oss/postgres/18/04_host.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/04_host.up.sql rename to internal/db/schema/migrations/oss/postgres/18/04_host.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/05_plugin_host.up.sql rename to internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/18/06_preferred_endpoints.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/06_preferred_endpoints.up.sql rename to internal/db/schema/migrations/oss/postgres/18/06_preferred_endpoints.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/17/07_wh_session_dimensions.up.sql b/internal/db/schema/migrations/oss/postgres/18/07_wh_session_dimensions.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/17/07_wh_session_dimensions.up.sql rename to internal/db/schema/migrations/oss/postgres/18/07_wh_session_dimensions.up.sql diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 43b9b72a2..3baebce12 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -164,11 +164,13 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er } if _, ok := currentServices[services.TargetService_ServiceDesc.ServiceName]; !ok { ts, err := targets.NewService( + ctx, c.kms, c.TargetRepoFn, c.IamRepoFn, c.ServersRepoFn, c.SessionRepoFn, + c.PluginHostRepoFn, c.StaticHostRepoFn, c.VaultCredentialRepoFn) if err != nil { diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 58df00e3b..b8b60321c 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -2637,7 +2637,6 @@ func TestAuthorizeSession(t *testing.T) { s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) require.NoError(t, err) - tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -2690,6 +2689,7 @@ func TestAuthorizeSession(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), tc.name) apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ Id: tar.GetPublicId(), Version: tar.GetVersion(), From 804cb22699f8b4da7912dca6eba36f1dbe1420d8 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 22 Oct 2021 15:45:46 -0400 Subject: [PATCH 37/71] Remove priority values from host_ip_address/host_dns_name (#1633) --- .../oss/postgres/18/05_plugin_host.up.sql | 18 +- internal/host/host_dns_address.go | 10 +- internal/host/host_ip_address.go | 10 +- internal/host/plugin/host.go | 62 +---- internal/host/plugin/host_address_test.go | 248 +++++------------- internal/host/plugin/repository_host.go | 8 +- internal/host/store/host.pb.go | 49 +--- .../storage/host/store/v1/host.proto | 10 +- 8 files changed, 95 insertions(+), 320 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql index 9d0855026..cc68eb046 100644 --- a/internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/18/05_plugin_host.up.sql @@ -209,15 +209,12 @@ begin; references host(public_id) on delete cascade on update cascade, - priority wt_priority, address inet not null, create_time wt_timestamp, - primary key (host_id, priority), - constraint host_ip_address_host_id_address_uq - unique(host_id, address) + primary key (host_id, address) ); comment on table host_ip_address is - 'host_ip_address entries are ip addresses set on a host with a preserved order.'; + 'host_ip_address entries are ip addresses set on a host.'; create trigger default_create_time_column before insert on host_ip_address for each row execute procedure default_create_time(); @@ -244,15 +241,12 @@ begin; references host(public_id) on delete cascade on update cascade, - priority wt_priority, name wt_dns_name, create_time wt_timestamp, - primary key (host_id, priority), - constraint host_dns_name_host_id_name_uq - unique(host_id, name) + primary key (host_id, name) ); comment on table host_dns_name is - 'host_dns_name entries are dns names set on a host with a preserved order.'; + 'host_dns_name entries are dns names set on a host'; create trigger default_create_time_column before insert on host_dns_name for each row execute procedure default_create_time(); @@ -359,8 +353,8 @@ begin; h.create_time, h.update_time, -- the string_agg(..) column will be null if there are no associated value objects - string_agg(distinct concat_ws('=', hip.priority, hip.address), '|') as ip_addresses, - string_agg(distinct concat_ws('=', hdns.priority, hdns.name), '|') as dns_names + string_agg(distinct host(hip.address), '|') as ip_addresses, + string_agg(distinct hdns.name, '|') as dns_names from host_plugin_host h join host_plugin_catalog hc on h.catalog_id = hc.public_id diff --git a/internal/host/host_dns_address.go b/internal/host/host_dns_address.go index a038478b0..3c7de2df5 100644 --- a/internal/host/host_dns_address.go +++ b/internal/host/host_dns_address.go @@ -17,13 +17,12 @@ type DnsName struct { tableName string `gorm:"-"` } -func NewDnsName(ctx context.Context, hostId string, priority uint32, name string) (*DnsName, error) { +func NewDnsName(ctx context.Context, hostId string, name string) (*DnsName, error) { const op = "host.NewDnsName" dn := &DnsName{ DnsName: &store.DnsName{ - HostId: hostId, - Name: name, - Priority: priority, + HostId: hostId, + Name: name, }, } if err := dn.validate(ctx, op); err != nil { @@ -37,9 +36,6 @@ func (dn *DnsName) validate(ctx context.Context, caller errors.Op) error { if dn.HostId == "" { return errors.New(ctx, errors.InvalidParameter, caller, "missing host id") } - if dn.Priority < 1 { - return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value") - } if dn.Name == "" { return errors.New(ctx, errors.InvalidParameter, caller, "missing dns name") } diff --git a/internal/host/host_ip_address.go b/internal/host/host_ip_address.go index d0644276e..0f7ba6b15 100644 --- a/internal/host/host_ip_address.go +++ b/internal/host/host_ip_address.go @@ -18,14 +18,13 @@ type IpAddress struct { tableName string `gorm:"-"` } -func NewIpAddress(ctx context.Context, hostId string, priority uint32, address string) (*IpAddress, error) { +func NewIpAddress(ctx context.Context, hostId string, address string) (*IpAddress, error) { const op = "host.NewIpAddress" ia := &IpAddress{ IpAddress: &store.IpAddress{ - HostId: hostId, - Address: address, - Priority: priority, + HostId: hostId, + Address: address, }, } if err := ia.validate(ctx, op); err != nil { @@ -39,9 +38,6 @@ func (ia *IpAddress) validate(ctx context.Context, caller errors.Op) error { if ia.HostId == "" { return errors.New(ctx, errors.InvalidParameter, caller, "missing host id") } - if ia.Priority < 1 { - return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value") - } if ia.Address == "" { return errors.New(ctx, errors.InvalidParameter, caller, "missing ip address") } diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go index 197eacef9..9cccc4839 100644 --- a/internal/host/plugin/host.go +++ b/internal/host/plugin/host.go @@ -2,13 +2,10 @@ package plugin import ( "context" - "fmt" "sort" - "strconv" "strings" "github.com/hashicorp/boundary/internal/db/timestamp" - "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" "google.golang.org/protobuf/proto" ) @@ -101,7 +98,6 @@ type hostAgg struct { func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { const op = "plugin.(hostAgg).toHost" const aggregateDelimiter = "|" - const priorityDelimiter = "=" h := allocHost() h.PublicId = agg.PublicId h.CatalogId = agg.CatalogId @@ -112,64 +108,14 @@ func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { h.CreateTime = agg.CreateTime h.UpdateTime = agg.UpdateTime - // This function is used to protect against someone messing with the order - // in the DB by doing some validation - prioritySortFunc := func(in []string) error { - var sortErr error - sort.Slice(in, func(i, j int) bool { - ini := strings.Split(in[i], priorityDelimiter) - if len(ini) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[i])) - return false - } - inj := strings.Split(in[j], priorityDelimiter) - if len(inj) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[j])) - return false - } - indexi, err := strconv.Atoi(ini[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - indexj, err := strconv.Atoi(inj[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - return indexi < indexj - }) - return sortErr - } - if agg.IpAddresses != "" { - ips := strings.Split(agg.IpAddresses, aggregateDelimiter) - if len(ips) > 0 { - if err := prioritySortFunc(ips); err != nil { - return nil, err - } - for i, ip := range ips { - // At this point they're in the correct order, but we still - // have to strip off the priority - ips[i] = strings.Split(ip, priorityDelimiter)[1] - } - h.IpAddresses = ips - } + h.IpAddresses = strings.Split(agg.IpAddresses, aggregateDelimiter) + sort.Strings(h.IpAddresses) } if agg.DnsNames != "" { - names := strings.Split(agg.DnsNames, aggregateDelimiter) - if len(names) > 0 { - if err := prioritySortFunc(names); err != nil { - return nil, err - } - for i, name := range names { - // At this point they're in the correct order, but we still - // have to strip off the priority - names[i] = strings.Split(name, priorityDelimiter)[1] - } - h.DnsNames = names - } + h.DnsNames = strings.Split(agg.DnsNames, aggregateDelimiter) + sort.Strings(h.DnsNames) } return h, nil diff --git a/internal/host/plugin/host_address_test.go b/internal/host/plugin/host_address_test.go index e4dc1079f..619075719 100644 --- a/internal/host/plugin/host_address_test.go +++ b/internal/host/plugin/host_address_test.go @@ -24,9 +24,8 @@ func TestHostDnsName_Create(t *testing.T) { host1 := testHost(t, conn, cat.GetPublicId(), "external") type args struct { - hostId string - name string - priority uint32 + hostId string + name string } tests := []struct { @@ -40,52 +39,31 @@ func TestHostDnsName_Create(t *testing.T) { { name: "blank-host-id-validate", args: args{ - hostId: "", - name: "foo.bar.com", - priority: 1, + hostId: "", + name: "foo.bar.com", }, wantNewErr: true, }, { name: "blank-name-validate", - args: args{ - hostId: host1.GetPublicId(), - name: "", - priority: 1, - }, - wantNewErr: true, - }, - { - name: "blank-priority-validate", args: args{ hostId: host1.GetPublicId(), - name: "foo.bar.com", + name: "", }, wantNewErr: true, }, { name: "blank-host-id-db", args: args{ - name: "foo.bar.com", - priority: 1, + name: "foo.bar.com", }, skipNewFunc: true, wantDbErr: true, }, { name: "blank-name-db", - args: args{ - hostId: host1.GetPublicId(), - priority: 1, - }, - skipNewFunc: true, - wantDbErr: true, - }, - { - name: "blank-priority-db", args: args{ hostId: host1.GetPublicId(), - name: "foo.bar.com", }, skipNewFunc: true, wantDbErr: true, @@ -93,46 +71,26 @@ func TestHostDnsName_Create(t *testing.T) { { name: "valid", args: args{ - hostId: host1.GetPublicId(), - name: "foo.bar.com", - priority: 1, + hostId: host1.GetPublicId(), + name: "foo.bar.com", }, want: &host.DnsName{ DnsName: &store.DnsName{ - HostId: host1.GetPublicId(), - Name: "foo.bar.com", - Priority: 1, + HostId: host1.GetPublicId(), + Name: "foo.bar.com", }, }, }, { name: "duplicate-name", args: args{ - hostId: host1.GetPublicId(), - name: "foo.bar.com", - priority: 2, + hostId: host1.GetPublicId(), + name: "foo.bar.com", }, want: &host.DnsName{ DnsName: &store.DnsName{ - HostId: host1.GetPublicId(), - Name: "foo.bar.com", - Priority: 2, - }, - }, - wantDbErr: true, - }, - { - name: "duplicate-priority", - args: args{ - hostId: host1.GetPublicId(), - name: "baz.bar.com", - priority: 1, - }, - want: &host.DnsName{ - DnsName: &store.DnsName{ - HostId: host1.GetPublicId(), - Name: "baz.bar.com", - Priority: 1, + HostId: host1.GetPublicId(), + Name: "foo.bar.com", }, }, wantDbErr: true, @@ -140,15 +98,13 @@ func TestHostDnsName_Create(t *testing.T) { { name: "valid-second", args: args{ - hostId: host1.GetPublicId(), - name: "baz.bar.com", - priority: 2, + hostId: host1.GetPublicId(), + name: "baz.bar.com", }, want: &host.DnsName{ DnsName: &store.DnsName{ - HostId: host1.GetPublicId(), - Name: "baz.bar.com", - Priority: 2, + HostId: host1.GetPublicId(), + Name: "baz.bar.com", }, }, }, @@ -161,7 +117,7 @@ func TestHostDnsName_Create(t *testing.T) { var got *host.DnsName var err error if !tt.skipNewFunc { - got, err = host.NewDnsName(ctx, tt.args.hostId, tt.args.priority, tt.args.name) + got, err = host.NewDnsName(ctx, tt.args.hostId, tt.args.name) if tt.wantNewErr { require.Error(err) return @@ -171,9 +127,8 @@ func TestHostDnsName_Create(t *testing.T) { } else { got = &host.DnsName{ DnsName: &store.DnsName{ - HostId: tt.args.hostId, - Name: tt.args.name, - Priority: tt.args.priority, + HostId: tt.args.hostId, + Name: tt.args.name, }, } } @@ -200,9 +155,8 @@ func TestHostIpAddress_Create(t *testing.T) { host1 := testHost(t, conn, cat.GetPublicId(), "external") type args struct { - hostId string - address string - priority uint32 + hostId string + address string } tests := []struct { @@ -216,26 +170,16 @@ func TestHostIpAddress_Create(t *testing.T) { { name: "blank-host-id-validate", args: args{ - hostId: "", - address: "1.2.3.4", - priority: 1, + hostId: "", + address: "1.2.3.4", }, wantNewErr: true, }, { name: "blank-name-validate", - args: args{ - hostId: host1.GetPublicId(), - address: "", - priority: 1, - }, - wantNewErr: true, - }, - { - name: "blank-priority-validate", args: args{ hostId: host1.GetPublicId(), - address: "1.2.3.4", + address: "", }, wantNewErr: true, }, @@ -250,8 +194,7 @@ func TestHostIpAddress_Create(t *testing.T) { { name: "blank-host-id-db", args: args{ - address: "1.2.3.4", - priority: 1, + address: "1.2.3.4", }, skipNewFunc: true, wantDbErr: true, @@ -259,17 +202,7 @@ func TestHostIpAddress_Create(t *testing.T) { { name: "blank-address-db", args: args{ - hostId: host1.GetPublicId(), - priority: 1, - }, - skipNewFunc: true, - wantDbErr: true, - }, - { - name: "blank-priority-db", - args: args{ - hostId: host1.GetPublicId(), - address: "1.2.3.4", + hostId: host1.GetPublicId(), }, skipNewFunc: true, wantDbErr: true, @@ -277,46 +210,26 @@ func TestHostIpAddress_Create(t *testing.T) { { name: "valid", args: args{ - hostId: host1.GetPublicId(), - address: "1.2.3.4", - priority: 1, + hostId: host1.GetPublicId(), + address: "1.2.3.4", }, want: &host.IpAddress{ IpAddress: &store.IpAddress{ - HostId: host1.GetPublicId(), - Address: "1.2.3.4", - Priority: 1, + HostId: host1.GetPublicId(), + Address: "1.2.3.4", }, }, }, { name: "duplicate-name", args: args{ - hostId: host1.GetPublicId(), - address: "1.2.3.4", - priority: 2, + hostId: host1.GetPublicId(), + address: "1.2.3.4", }, want: &host.IpAddress{ IpAddress: &store.IpAddress{ - HostId: host1.GetPublicId(), - Address: "1.2.3.4", - Priority: 2, - }, - }, - wantDbErr: true, - }, - { - name: "duplicate-priority", - args: args{ - hostId: host1.GetPublicId(), - address: "2.3.4.5", - priority: 1, - }, - want: &host.IpAddress{ - IpAddress: &store.IpAddress{ - HostId: host1.GetPublicId(), - Address: "2.3.4.5", - Priority: 1, + HostId: host1.GetPublicId(), + Address: "1.2.3.4", }, }, wantDbErr: true, @@ -324,15 +237,13 @@ func TestHostIpAddress_Create(t *testing.T) { { name: "valid-second", args: args{ - hostId: host1.GetPublicId(), - address: "2.3.4.5", - priority: 2, + hostId: host1.GetPublicId(), + address: "2.3.4.5", }, want: &host.IpAddress{ IpAddress: &store.IpAddress{ - HostId: host1.GetPublicId(), - Address: "2.3.4.5", - Priority: 2, + HostId: host1.GetPublicId(), + Address: "2.3.4.5", }, }, }, @@ -345,7 +256,7 @@ func TestHostIpAddress_Create(t *testing.T) { var got *host.IpAddress var err error if !tt.skipNewFunc { - got, err = host.NewIpAddress(ctx, tt.args.hostId, tt.args.priority, tt.args.address) + got, err = host.NewIpAddress(ctx, tt.args.hostId, tt.args.address) if tt.wantNewErr { require.Error(err) return @@ -355,9 +266,8 @@ func TestHostIpAddress_Create(t *testing.T) { } else { got = &host.IpAddress{ IpAddress: &store.IpAddress{ - HostId: tt.args.hostId, - Address: tt.args.address, - Priority: tt.args.priority, + HostId: tt.args.hostId, + Address: tt.args.address, }, } } @@ -382,14 +292,13 @@ func TestHostDnsName_Delete(t *testing.T) { plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) host1 := testHost(t, conn, cat.GetPublicId(), "external") - addr1, err := host.NewDnsName(ctx, host1.GetPublicId(), 1, "addr1.foo.com") + addr1, err := host.NewDnsName(ctx, host1.GetPublicId(), "addr1.foo.com") require.NoError(t, err) require.NoError(t, w.Create(ctx, addr1)) type args struct { - hostId string - name string - priority uint32 + hostId string + name string } tests := []struct { @@ -401,33 +310,15 @@ func TestHostDnsName_Delete(t *testing.T) { { name: "wrong_host_id", args: args{ - hostId: "something", - name: addr1.GetName(), - priority: 1, - }, - }, - { - name: "missing_priority", - args: args{ - hostId: addr1.GetHostId(), + hostId: "something", name: addr1.GetName(), }, - wantError: true, - }, - { - name: "wrong_priority", - args: args{ - hostId: addr1.GetHostId(), - name: addr1.GetName(), - priority: 2, - }, }, { name: "valid", args: args{ - hostId: addr1.GetHostId(), - name: addr1.GetName(), - priority: 1, + hostId: addr1.GetHostId(), + name: addr1.GetName(), }, wantDelete: true, }, @@ -438,9 +329,8 @@ func TestHostDnsName_Delete(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got := &host.DnsName{ DnsName: &store.DnsName{ - HostId: tt.args.hostId, - Name: tt.args.name, - Priority: tt.args.priority, + HostId: tt.args.hostId, + Name: tt.args.name, }, } require.NoError(t, err) @@ -469,14 +359,13 @@ func TestHostIpAddress_Delete(t *testing.T) { plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) host1 := testHost(t, conn, cat.GetPublicId(), "external") - addr1, err := host.NewIpAddress(ctx, host1.GetPublicId(), 1, "1.2.3.4") + addr1, err := host.NewIpAddress(ctx, host1.GetPublicId(), "1.2.3.4") require.NoError(t, err) require.NoError(t, w.Create(ctx, addr1)) type args struct { - hostId string - address string - priority uint32 + hostId string + address string } tests := []struct { @@ -488,33 +377,15 @@ func TestHostIpAddress_Delete(t *testing.T) { { name: "wrong_host_id", args: args{ - hostId: "something", - address: addr1.GetAddress(), - priority: 1, - }, - }, - { - name: "missing_priority", - args: args{ - hostId: addr1.GetHostId(), + hostId: "something", address: addr1.GetAddress(), }, - wantError: true, - }, - { - name: "wrong_priority", - args: args{ - hostId: addr1.GetHostId(), - address: addr1.GetAddress(), - priority: 2, - }, }, { name: "valid", args: args{ - hostId: addr1.GetHostId(), - address: addr1.GetAddress(), - priority: 1, + hostId: addr1.GetHostId(), + address: addr1.GetAddress(), }, wantDelete: true, }, @@ -525,9 +396,8 @@ func TestHostIpAddress_Delete(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got := &host.IpAddress{ IpAddress: &store.IpAddress{ - HostId: tt.args.hostId, - Address: tt.args.address, - Priority: tt.args.priority, + HostId: tt.args.hostId, + Address: tt.args.address, }, } require.NoError(t, err) diff --git a/internal/host/plugin/repository_host.go b/internal/host/plugin/repository_host.go index 2979d87b4..be6cc4214 100644 --- a/internal/host/plugin/repository_host.go +++ b/internal/host/plugin/repository_host.go @@ -83,8 +83,8 @@ func (r *Repository) UpsertHosts( if len(h.GetIpAddresses()) > 0 { sort.Strings(h.IpAddresses) ipAddresses = make([]interface{}, 0, len(h.GetIpAddresses())) - for i, a := range h.GetIpAddresses() { - obj, err := host.NewIpAddress(ctx, h.PublicId, uint32(i+1), a) + for _, a := range h.GetIpAddresses() { + obj, err := host.NewIpAddress(ctx, h.PublicId, a) if err != nil { return nil, errors.Wrap(ctx, err, op) } @@ -96,8 +96,8 @@ func (r *Repository) UpsertHosts( if len(h.GetDnsNames()) > 0 { sort.Strings(h.DnsNames) dnsNames = make([]interface{}, 0, len(h.GetDnsNames())) - for i, n := range h.GetDnsNames() { - obj, err := host.NewDnsName(ctx, h.PublicId, uint32(i+1), n) + for _, n := range h.GetDnsNames() { + obj, err := host.NewDnsName(ctx, h.PublicId, n) if err != nil { return nil, errors.Wrap(ctx, err, op) } diff --git a/internal/host/store/host.pb.go b/internal/host/store/host.pb.go index c9c8fcaf8..2434f98a6 100644 --- a/internal/host/store/host.pb.go +++ b/internal/host/store/host.pb.go @@ -276,9 +276,7 @@ type IpAddress struct { // @inject_tag: `gorm:"primary_key"` HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"` // @inject_tag: `gorm:"primary_key"` - Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"` - // @inject_tag: `gorm:"not_null` - Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty" gorm:"primary_key"` } func (x *IpAddress) Reset() { @@ -320,13 +318,6 @@ func (x *IpAddress) GetHostId() string { return "" } -func (x *IpAddress) GetPriority() uint32 { - if x != nil { - return x.Priority - } - return 0 -} - func (x *IpAddress) GetAddress() string { if x != nil { return x.Address @@ -342,9 +333,7 @@ type DnsName struct { // @inject_tag: `gorm:"primary_key"` HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"` // @inject_tag: `gorm:"primary_key"` - Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"` - // @inject_tag: `gorm:"not_null` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" gorm:"primary_key"` } func (x *DnsName) Reset() { @@ -386,13 +375,6 @@ func (x *DnsName) GetHostId() string { return "" } -func (x *DnsName) GetPriority() uint32 { - if x != nil { - return x.Priority - } - return 0 -} - func (x *DnsName) GetName() string { if x != nil { return x.Name @@ -427,22 +409,19 @@ var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{ 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x07, 0x44, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x36, 0x0a, 0x07, 0x44, 0x6e, 0x73, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/internal/proto/local/controller/storage/host/store/v1/host.proto b/internal/proto/local/controller/storage/host/store/v1/host.proto index deb95f14b..0f89ebaed 100644 --- a/internal/proto/local/controller/storage/host/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/store/v1/host.proto @@ -53,10 +53,7 @@ message IpAddress { string host_id = 1; // @inject_tag: `gorm:"primary_key"` - uint32 priority = 2; - - // @inject_tag: `gorm:"not_null` - string address = 3; + string address = 2; } message DnsName { @@ -64,8 +61,5 @@ message DnsName { string host_id = 1; // @inject_tag: `gorm:"primary_key"` - uint32 priority = 2; - - // @inject_tag: `gorm:"not_null` - string name = 3; + string name = 2; } \ No newline at end of file From 8795b20fe2d83258413dd063316c663a9f260272 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 25 Oct 2021 09:37:05 -0700 Subject: [PATCH 38/71] Delete actions for plugin based catalogs and sets (#1622) * Add ability to delete plugin based host catalogs and host sets * Add plugin host catalog and set tests for API. --- go.mod | 144 +++++++++++++++-- .../host/plugin/repository_host_catalog.go | 74 +++++++++ .../plugin/repository_host_catalog_test.go | 152 ++++++++++++++++++ internal/host/plugin/repository_host_set.go | 79 +++++++++ .../host/plugin/repository_host_set_test.go | 87 ++++++++++ internal/servers/controller/controller.go | 2 +- .../host_catalogs/host_catalog_service.go | 27 +++- .../host_catalog_service_test.go | 72 ++++++++- .../handlers/host_sets/host_set_service.go | 29 +++- .../host_sets/host_set_service_test.go | 74 ++++++++- .../api/hostcatalogs/host_catalog_test.go | 17 ++ internal/tests/api/hostsets/host_set_test.go | 22 ++- 12 files changed, 745 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index af6584489..efbfb1cc1 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,14 @@ module github.com/hashicorp/boundary -go 1.16 +go 1.17 replace github.com/hashicorp/boundary/api => ./api replace github.com/hashicorp/boundary/sdk => ./sdk require ( - github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.21 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/armon/go-metrics v0.3.9 - github.com/aws/aws-sdk-go v1.40.44 // indirect github.com/bufbuild/buf v0.37.0 - github.com/dhui/dktest v0.3.4 // indirect github.com/fatih/color v1.12.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.3.0 @@ -24,7 +17,7 @@ require ( github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hashicorp/boundary/api v0.0.19 github.com/hashicorp/boundary/sdk v0.0.12-0.20211007161358-65f3725bf3e1 @@ -40,7 +33,6 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-rootcerts v1.0.2 - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 @@ -52,7 +44,6 @@ require ( github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 github.com/hashicorp/go-uuid v1.0.2 - github.com/hashicorp/go-version v1.3.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/vault/api v1.1.1 github.com/hashicorp/vault/sdk v0.2.1 @@ -77,7 +68,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/posener/complete v1.2.3 github.com/ryanuber/go-glob v1.0.0 - github.com/spf13/cobra v1.1.1 // indirect github.com/stretchr/testify v1.7.0 github.com/zalando/go-keyring v0.1.1 go.uber.org/atomic v1.9.0 @@ -85,7 +75,6 @@ require ( golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 golang.org/x/term v0.0.0-20210916214954-140adaaadfaf golang.org/x/tools v0.1.6 - google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 google.golang.org/grpc v1.40.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 @@ -95,3 +84,132 @@ require ( mvdan.cc/gofumpt v0.1.1 nhooyr.io/websocket v1.8.7 ) + +require ( + cloud.google.com/go v0.65.0 // indirect + github.com/AlecAivazis/survey/v2 v2.2.9 // indirect + github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect + github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.21 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.14 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/Microsoft/go-winio v0.4.16 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f // indirect + github.com/apex/log v1.9.0 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go v1.40.44 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.1.0 // indirect + github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc // indirect + github.com/coreos/go-oidc/v3 v3.0.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/danieljoos/wincred v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dhui/dktest v0.3.4 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/docker/cli v20.10.7+incompatible // indirect + github.com/docker/docker v20.10.7+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect + github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/gofrs/flock v0.8.0 // indirect + github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.0.0 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.1.0 // indirect + github.com/hashicorp/go-plugin v1.0.1 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect + github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-version v1.3.0 // indirect + github.com/hashicorp/golang-lru v0.5.3 // indirect + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.7.0 // indirect + github.com/jefferai/go-libsecret v0.0.0-20210105015933-d08a58b018bc // indirect + github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect + github.com/jhump/protoreflect v1.8.1 // indirect + github.com/jinzhu/gorm v1.9.12 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.2 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.10 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect + github.com/klauspost/compress v1.11.7 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/lib/pq v1.8.0 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/iochan v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v1.0.0-rc9 // indirect + github.com/oracle/oci-go-sdk v12.5.0+incompatible // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/pkg/profile v1.5.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.6.2 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/sirupsen/logrus v1.7.0 // indirect + github.com/spf13/cobra v1.1.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/twitchtv/twirp v7.1.0+incompatible // indirect + github.com/urfave/cli/v2 v2.3.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + go.opencensus.io v0.22.6 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.16.0 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect + golang.org/x/text v0.3.6 // indirect + golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/api v0.30.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index f788fe7ee..a7353eaf9 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -195,6 +195,80 @@ func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt .. return hostCatalogs, plgs, nil } +// DeleteCatalog deletes catalog for the provided id from the repository +// returning a count of the number of records deleted. All options are ignored. +func (r *Repository) DeleteCatalog(ctx context.Context, id string, _ ...Option) (int, error) { + const op = "plugin.(Repository).DeleteCatalog" + if id == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + + c, err := r.getCatalog(ctx, id) + if err != nil && !errors.IsNotFoundError(err) { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + if c == nil { + return db.NoRowsAffected, nil + } + p, err := r.getPersistedDataForCatalog(ctx, c) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + plgClient, ok := r.plugins[c.GetPluginId()] + if !ok || plgClient == nil { + return 0, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) + } + plgHc, err := toPluginCatalog(ctx, c) + if err != nil { + return 0, errors.Wrap(ctx, err, op) + } + // TODO: Include all the sets for this catalog in the delete catalog request. We don't need it for now + // since our currently provided plugins only read data for set configuration, but in the future that might + // change. + _, err = plgClient.OnDeleteCatalog(ctx, &plgpb.OnDeleteCatalogRequest{Catalog: plgHc, Persisted: p}) + if err != nil { + // Even if the plugin returns an error, we ignore it and proceed with + // deleting the catalog. + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + metadata := c.oplog(oplog.OpType_OP_TYPE_DELETE) + + var rowsDeleted int + var deleteCatalog *HostCatalog + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + deleteCatalog = c.clone() + var err error + rowsDeleted, err = w.Delete( + ctx, + deleteCatalog, + db.WithOplog(oplogWrapper, metadata), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsDeleted > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil + }, + ) + + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("delete failed for %s", c.PublicId))) + } + + return rowsDeleted, nil +} + // getCatalog retrieves the *HostCatalog with the provided id. If it is not found or there // is an problem getting it from the database an error is returned instead. func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, error) { diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 58993a303..15f606bdc 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "fmt" "strings" "testing" "time" @@ -470,3 +471,154 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { assert.Equal(t, total, len(got)) assert.Contains(t, plgs, plg) } + +func TestRepository_DeleteCatalog(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "test") + pluginInstance := &TestPluginServer{} + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{Server: pluginInstance}, + } + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + badId, err := newHostCatalogId(ctx) + assert.NoError(t, err) + assert.NotNil(t, badId) + + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, plgm) + assert.NoError(t, err) + assert.NotNil(t, repo) + + tests := []struct { + name string + id string + pluginChecker func(*testing.T, *plgpb.OnDeleteCatalogRequest) error + want int + wantErr errors.Code + }{ + { + name: "found", + id: cat.GetPublicId(), + pluginChecker: func(t *testing.T, req *plgpb.OnDeleteCatalogRequest) error { + assert.Equal(t, cat.GetPublicId(), req.GetCatalog().GetId()) + return nil + }, + want: 1, + }, + { + name: "ignore error", + id: cat2.GetPublicId(), + pluginChecker: func(t *testing.T, req *plgpb.OnDeleteCatalogRequest) error { + assert.Equal(t, cat2.GetPublicId(), req.GetCatalog().GetId()) + return fmt.Errorf("This is a test error") + }, + want: 1, + }, + { + name: "not-found", + id: badId, + pluginChecker: func(t *testing.T, req *plgpb.OnDeleteCatalogRequest) error { + assert.Fail(t, "Should not call the plugin when catalog isn't found") + return nil + }, + want: 0, + }, + { + name: "bad-public-id", + id: "", + pluginChecker: func(t *testing.T, req *plgpb.OnDeleteCatalogRequest) error { + assert.Fail(t, "Should not call the plugin for a bad id") + return nil + }, + want: 0, + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + pluginInstance.OnDeleteCatalogFn = func(_ context.Context, request *plgpb.OnDeleteCatalogRequest) (*plgpb.OnDeleteCatalogResponse, error) { + return nil, tt.pluginChecker(t, request) + } + got, err := repo.DeleteCatalog(context.Background(), tt.id) + if tt.wantErr != 0 { + assert.Truef(t, errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.want, got, "row count") + }) + } +} + +func TestRepository_DeleteCatalogX(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + plg := hostplg.TestPlugin(t, conn, "test") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{Server: &TestPluginServer{}}, + } + cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + badId, err := newHostCatalogId(ctx) + assert.NoError(t, err) + assert.NotNil(t, badId) + + tests := []struct { + name string + id string + want *HostCatalog + wantErr errors.Code + }{ + { + name: "found", + id: cat.GetPublicId(), + want: cat, + }, + { + name: "not-found", + id: badId, + want: nil, + }, + { + name: "bad-public-id", + id: "", + want: nil, + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, plgm) + assert.NoError(err) + assert.NotNil(repo) + + got, _, err := repo.LookupCatalog(ctx, tt.id) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(err) + + switch { + case tt.want == nil: + assert.Nil(got) + case tt.want != nil: + assert.NotNil(got) + assert.Equal(got, tt.want) + } + }) + } +} \ No newline at end of file diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 06fecbeb3..b89821dce 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -249,6 +249,85 @@ func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host return sets, plg, nil } +// DeleteSet deletes the host set for the provided id from the repository +// returning a count of the number of records deleted. All options are +// ignored. +func (r *Repository) DeleteSet(ctx context.Context, scopeId string, publicId string, _ ...Option) (int, error) { + const op = "plugin.(Repository).DeleteSet" + if publicId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + if scopeId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + + sets, plg, err := r.getSets(ctx, publicId, "") + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + if len(sets) != 1 { + return db.NoRowsAffected, nil + } + s := sets[0] + + c, err := r.getCatalog(ctx, s.GetCatalogId()) + if err != nil && errors.IsNotFoundError(err) { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + if c == nil { + return db.NoRowsAffected, nil + } + p, err := r.getPersistedDataForCatalog(ctx, c) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + plgClient, ok := r.plugins[plg.GetPublicId()] + if !ok || plgClient == nil { + return 0, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) + } + plgHc, err := toPluginCatalog(ctx, c) + if err != nil { + return 0, errors.Wrap(ctx, err, op) + } + plgHs, err := toPluginSet(ctx, s) + if err != nil { + return 0, errors.Wrap(ctx, err, op) + } + _, err = plgClient.OnDeleteSet(ctx, &plgpb.OnDeleteSetRequest{Catalog: plgHc, Persisted: p, Set: plgHs}) + if err != nil { + // Even if the plugin returns an error, we ignore it and proceed + // with deleting the set. + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsDeleted int + _, err = r.writer.DoTx( + ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) (err error) { + ds := s.clone() + rowsDeleted, err = w.Delete(ctx, ds, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_DELETE))) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsDeleted > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil + }, + ) + + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("delete failed for %s", s.PublicId))) + } + + return rowsDeleted, nil +} + func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).getSets" if publicId == "" && catalogId == "" { diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index cffa56bc9..c915f5d93 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "fmt" "sort" "strings" "testing" @@ -654,6 +655,92 @@ func TestRepository_ListSets_Limits(t *testing.T) { } } +func TestRepository_DeleteSet(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "create") + + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + return &plgpb.OnCreateSetResponse{}, nil + }}), + } + c := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + hostSet := TestSet(t, conn, kms, c, plgm) + hostSet2 := TestSet(t, conn, kms, c, plgm) + + newHostSetId, err := newHostSetId(ctx) + require.NoError(t, err) + tests := []struct { + name string + in string + pluginChecker func(*plgpb.OnDeleteSetRequest) error + want int + wantIsErr errors.Code + }{ + { + name: "With no public id", + pluginChecker: func(req *plgpb.OnDeleteSetRequest) error { + assert.Fail(t, "The plugin shouldn't be called") + return nil + }, + wantIsErr: errors.InvalidParameter, + }, + { + name: "With non existing host set id", + in: newHostSetId, + pluginChecker: func(req *plgpb.OnDeleteSetRequest) error { + assert.Fail(t, "The plugin shouldn't be called") + return nil + }, + want: 0, + }, + { + name: "With existing host set id", + in: hostSet.PublicId, + pluginChecker: func(req *plgpb.OnDeleteSetRequest) error { + assert.Equal(t, c.GetPublicId(), req.GetCatalog().GetId()) + assert.Equal(t, hostSet.GetPublicId(), req.GetSet().GetId()) + return nil + }, + want: 1, + }, + { + name: "Ignores plugin errors", + in: hostSet2.PublicId, + pluginChecker: func(req *plgpb.OnDeleteSetRequest) error { + assert.Equal(t, c.GetPublicId(), req.GetCatalog().GetId()) + assert.Equal(t, hostSet2.GetPublicId(), req.GetSet().GetId()) + return fmt.Errorf("test error") + }, + want: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(rw, rw, kms, plgm) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.DeleteSet(ctx, prj.PublicId, tt.in) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Zero(got) + return + } + require.NoError(err) + assert.EqualValues(tt.want, got) + }) + } +} + func printoutTable(t *testing.T, rw *db.Db) { ctx := context.Background() hsas := []*hostSetAgg{} diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 7e6175878..ca29bbcc0 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -123,7 +123,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { external_host_plugins.WithHostPluginExecutionDir(conf.RawConfig.Plugins.ExecutionDir), external_host_plugins.WithLogger(hclog.NewNullLogger())) if err != nil { - return nil, fmt.Errorf("error creating azure host plugin") + return nil, fmt.Errorf("error creating azure host plugin: %w", err) } conf.ShutdownFuncs = append(conf.ShutdownFuncs, azureCleanup) if _, err := conf.RegisterHostPlugin(ctx, "azure", azureSvcClient, hostplugin.WithDescription("Built-in Azure host plugin")); err != nil { diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 7531926ea..86360a124 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -534,13 +534,26 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { const op = "host_catalogs.(Service).deleteFromRepo" - repo, err := s.staticRepoFn() - if err != nil { - return false, errors.Wrap(ctx, err, op) - } - rows, err := repo.DeleteCatalog(ctx, id) - if err != nil { - return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + rows := 0 + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return false, errors.Wrap(ctx, err, op) + } + rows, err = repo.DeleteCatalog(ctx, id) + if err != nil { + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + } + case plugin.Subtype: + repo, err := s.pluginHostRepoFn() + if err != nil { + return false, errors.Wrap(ctx, err, op) + } + rows, err = repo.DeleteCatalog(ctx, id) + if err != nil { + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + } } return rows > 0, nil } diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 01650a4f3..3d2645035 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -455,7 +455,7 @@ func TestList(t *testing.T) { } } -func TestDelete(t *testing.T) { +func TestDelete_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -524,6 +524,76 @@ func TestDelete(t *testing.T) { } } +func TestDelete_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + plg := host.TestPlugin(t, conn, "test") + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + + s, err := host_catalogs.NewService(repo, pluginHostRepo, pluginRepo, iamRepoFn) + require.NoError(t, err, "Couldn't create a new host catalog service.") + + cases := []struct { + name string + scopeId string + req *pbs.DeleteHostCatalogRequest + res *pbs.DeleteHostCatalogResponse + err error + }{ + { + name: "Delete an Existing HostCatalog", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostCatalogRequest{ + Id: hc.GetPublicId(), + }, + }, + { + name: "Delete bad id HostCatalog", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostCatalogRequest{ + Id: plugin.HostCatalogPrefix + "_doesntexis", + }, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Bad HostCatalog Id formatting", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostCatalogRequest{ + Id: plugin.HostCatalogPrefix + "_bad_format", + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, gErr := s.DeleteHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "DeleteHostCatalog(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + assert.EqualValuesf(tc.res, got, "DeleteHostCatalog(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + func TestDelete_twice(t *testing.T) { t.Parallel() assert, require := assert.New(t), require.New(t) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index f9a02d512..cd047b20a 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -525,13 +525,26 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, re func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) { const op = "host_sets.(Service).deleteFromRepo" - repo, err := s.staticRepoFn() - if err != nil { - return false, err - } - rows, err := repo.DeleteSet(ctx, scopeId, id) - if err != nil { - return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + rows := 0 + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return false, err + } + rows, err = repo.DeleteSet(ctx, scopeId, id) + if err != nil { + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + } + case plugin.Subtype: + repo, err := s.pluginRepoFn() + if err != nil { + return false, err + } + rows, err = repo.DeleteSet(ctx, scopeId, id) + if err != nil { + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete host")) + } } return rows > 0, nil } @@ -900,7 +913,7 @@ func validateUpdateRequest(ctx context.Context, req *pbs.UpdateHostSetRequest) e } func validateDeleteRequest(req *pbs.DeleteHostSetRequest) error { - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix) + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, plugin.HostSetPrefix) } func validateListRequest(req *pbs.ListHostSetsRequest) error { diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 6fd73e248..d7c630028 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -445,7 +445,7 @@ func TestList_Plugin(t *testing.T) { } } -func TestDelete(t *testing.T) { +func TestDelete_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -494,18 +494,86 @@ func TestDelete(t *testing.T) { err: handlers.ApiErrorWithCode(codes.NotFound), }, { - name: "Delete bad host catalog id in Host Set", + name: "Bad Host Id formatting", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostSetRequest{ + Id: static.HostSetPrefix + "_bad_format", + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, gErr := s.DeleteHostSet(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "DeleteHostSet(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + assert.Empty(cmp.Diff(tc.res, got, protocmp.Transform()), "DeleteHostSet(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + +func TestDelete_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + _, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + } + name := "test" + plg := hostplugin.TestPlugin(t, conn, name) + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + } + + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + h := plugin.TestSet(t, conn, kms, hc, plgm) + + s, err := host_sets.NewService(repoFn, pluginRepoFn) + require.NoError(t, err, "Couldn't create a new host set service.") + + cases := []struct { + name string + scopeId string + req *pbs.DeleteHostSetRequest + res *pbs.DeleteHostSetResponse + err error + }{ + { + name: "Delete an Existing Host Set", scopeId: proj.GetPublicId(), req: &pbs.DeleteHostSetRequest{ Id: h.GetPublicId(), }, + }, + { + name: "Delete bad id Host Set", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostSetRequest{ + Id: plugin.HostSetPrefix + "_doesntexis", + }, err: handlers.ApiErrorWithCode(codes.NotFound), }, { name: "Bad Host Id formatting", scopeId: proj.GetPublicId(), req: &pbs.DeleteHostSetRequest{ - Id: static.HostSetPrefix + "_bad_format", + Id: plugin.HostSetPrefix + "_bad_format", }, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, diff --git a/internal/tests/api/hostcatalogs/host_catalog_test.go b/internal/tests/api/hostcatalogs/host_catalog_test.go index e37dbe13b..12fd5679c 100644 --- a/internal/tests/api/hostcatalogs/host_catalog_test.go +++ b/internal/tests/api/hostcatalogs/host_catalog_test.go @@ -117,6 +117,23 @@ func TestCrud(t *testing.T) { apiErr := api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) + + // Plugin catalogs + c, err := hcClient.Create(tc.Context(), "plugin", proj.GetPublicId(), hostcatalogs.WithName("pluginfoo"), hostcatalogs.WithPluginId("pl_1234567890"), + hostcatalogs.WithAttributes(map[string]interface{}{"foo": "bar"})) + require.NoError(err) + + c, err = hcClient.Read(tc.Context(), c.Item.Id) + assert.NoError(err) + + _, err = hcClient.Delete(tc.Context(), c.Item.Id) + assert.NoError(err) + + _, err = hcClient.Delete(tc.Context(), c.Item.Id) + require.Error(err) + apiErr = api.AsServerError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) } func TestErrors(t *testing.T) { diff --git a/internal/tests/api/hostsets/host_set_test.go b/internal/tests/api/hostsets/host_set_test.go index e44501839..eb78af142 100644 --- a/internal/tests/api/hostsets/host_set_test.go +++ b/internal/tests/api/hostsets/host_set_test.go @@ -127,7 +127,8 @@ func TestCrud(t *testing.T) { client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + hcClient := hostcatalogs.NewClient(client) + hc, err := hcClient.Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.NotNil(hc) @@ -164,6 +165,25 @@ func TestCrud(t *testing.T) { apiErr := api.AsServerError(err) require.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) + + // Plugin Sets + c, err := hcClient.Create(tc.Context(), "plugin", proj.GetPublicId(), hostcatalogs.WithName("pluginfoo"), hostcatalogs.WithPluginId("pl_1234567890"), + hostcatalogs.WithAttributes(map[string]interface{}{"foo": "bar"})) + require.NoError(err) + + h, err = hClient.Create(tc.Context(), c.Item.Id, hostsets.WithName("foo")) + checkHost(t, "create", h.Item, err, "foo", 1) + + h, err = hClient.Read(tc.Context(), h.Item.Id) + checkHost(t, "read", h.Item, err, "foo", 1) + + _, err = hClient.Delete(tc.Context(), h.Item.Id) + assert.NoError(err) + _, err = hClient.Delete(tc.Context(), h.Item.Id) + require.Error(err) + apiErr = api.AsServerError(err) + require.NotNil(apiErr) + assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) } // TODO: Get better coverage for expected errors and error formats. From 86f2535aaf37e7d0338acd5a356aa0ca10461b64 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Mon, 25 Oct 2021 10:36:58 -0700 Subject: [PATCH 39/71] internal/libs/patchstruct: add package for patching structpb (#1592) * internal/libs/patchstruct: add package for patching structpb This adds a small helper package for patching *structpb.Struct values using the same patterns that our APIs use for updating records. The intent of this package is to provide a fallback mechanism for working with these values when said record update behavior is unavailable, such as when working with plugin-based host subtypes where the exact structure of the subtype's attributes is unavailable. patchstruct.Patch follows the following logic: * The source (src) map is merged into the destination map (dst). * Values are overwritten by the source map if they exist in both. * Values are deleted from the destination if they are set to null in the source. * Maps are recursively applied, meaning that a nested map at key "foo" in the destination would be patched with a map at key "foo" in the source. * A map in the destination is overwritten by a non-map in the source, and a non-map in the destination is overwritten by a map in the source. Patch returns the updated map as a copy, dst and src are not altered. * Add doc comment for patchstruct.Patch * vim snafu * Premature commit of repository_host_catalog.go * add nil cases * internal/libs/patchstruct: add PatchJSON * small comment formatting fix * Change PatchJSON to PatchBytes PatchJSON wasn't what was needed here - we need a helper to patch the protobuf encoding of structpb.Struct. * remove equality for original src/dst for patchbytes --- internal/libs/patchstruct/patchstruct.go | 90 ++++++++ internal/libs/patchstruct/patchstruct_test.go | 198 ++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 internal/libs/patchstruct/patchstruct.go create mode 100644 internal/libs/patchstruct/patchstruct_test.go diff --git a/internal/libs/patchstruct/patchstruct.go b/internal/libs/patchstruct/patchstruct.go new file mode 100644 index 000000000..74798006d --- /dev/null +++ b/internal/libs/patchstruct/patchstruct.go @@ -0,0 +1,90 @@ +package patchstruct + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +// PatchStruct updates the struct found in dst with the values found in src. +// The intent of this helper is to provide a fallback mechanism for subtype +// attributes when the actual schema of the subtype attributes are unknown. As +// such, it's preferred to use other methods (such as mask mapping) when an +// actual message for the subtype is known. +// +// The following rules apply: +// +// * The source (src) map is merged into the destination map (dst). +// +// * Values are overwritten by the source map if they exist in both. +// +// * Values are deleted from the destination if they are set to null in the +// source. +// +// * Maps are recursively applied, meaning that a nested map at key "foo" in +// the destination would be patched with a map at key "foo" in the +// source. +// +// * A map in the destination is overwritten by a non-map in the source, +// and a non-map in the destination is overwritten by a map in the +// source. +// +// PatchStruct returns the updated map as a copy, dst and src are not altered. +func PatchStruct(dst, src *structpb.Struct) *structpb.Struct { + result, err := structpb.NewStruct(patchM(dst.AsMap(), src.AsMap())) + if err != nil { + // Should never error as values are source from structpb values + panic(err) + } + + return result +} + +// PatchBytes follows the same rules as above with PatchStruct, but +// instead of patching structpb.Structs, it patches the protobuf +// encoding. An error is returned if there are issues working with +// the data. +func PatchBytes(dst, src []byte) ([]byte, error) { + srcpb, dstpb := new(structpb.Struct), new(structpb.Struct) + if err := proto.Unmarshal(dst, dstpb); err != nil { + return nil, fmt.Errorf("error reading destination data: %w", err) + } + + if err := proto.Unmarshal(src, srcpb); err != nil { + return nil, fmt.Errorf("error reading source data: %w", err) + } + + result, err := proto.Marshal(PatchStruct(dstpb, srcpb)) + if err != nil { + return nil, fmt.Errorf("error writing result data: %w", err) + } + + return result, nil +} + +func patchM(dst, src map[string]interface{}) map[string]interface{} { + for k, v := range src { + switch x := v.(type) { + case map[string]interface{}: + if y, ok := dst[k].(map[string]interface{}); ok { + // If the value in dst a map, continue to patch + dst[k] = patchM(y, x) + } else { + // Overwrite + dst[k] = x + } + + default: + if v == nil { + // explicit null values delete values at that key in dst + delete(dst, k) + } else { + // Anything else gets overwritten + dst[k] = v + } + } + } + + return dst +} diff --git a/internal/libs/patchstruct/patchstruct_test.go b/internal/libs/patchstruct/patchstruct_test.go new file mode 100644 index 000000000..c043be413 --- /dev/null +++ b/internal/libs/patchstruct/patchstruct_test.go @@ -0,0 +1,198 @@ +package patchstruct + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +type testCase struct { + name string + dst map[string]interface{} + src map[string]interface{} + expected map[string]interface{} +} + +var testCases = []testCase{ + { + name: "merge", + dst: map[string]interface{}{ + "foo": "bar", + }, + src: map[string]interface{}{ + "baz": "qux", + }, + expected: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + { + name: "overwrite", + dst: map[string]interface{}{ + "foo": "bar", + }, + src: map[string]interface{}{ + "foo": "baz", + }, + expected: map[string]interface{}{ + "foo": "baz", + }, + }, + { + name: "delete", + dst: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + src: map[string]interface{}{ + "baz": nil, + }, + expected: map[string]interface{}{ + "foo": "bar", + }, + }, + { + name: "recursive", + dst: map[string]interface{}{ + "nested": map[string]interface{}{ + "a": "b", + }, + "foo": "bar", + }, + src: map[string]interface{}{ + "nested": map[string]interface{}{ + "c": "d", + }, + }, + expected: map[string]interface{}{ + "nested": map[string]interface{}{ + "a": "b", + "c": "d", + }, + "foo": "bar", + }, + }, + { + name: "overwrite with map in src", + dst: map[string]interface{}{ + "foo": "bar", + }, + src: map[string]interface{}{ + "foo": map[string]interface{}{ + "a": "b", + }, + }, + expected: map[string]interface{}{ + "foo": map[string]interface{}{ + "a": "b", + }, + }, + }, + { + name: "nil src", + dst: map[string]interface{}{ + "foo": "bar", + }, + src: nil, + expected: map[string]interface{}{ + "foo": "bar", + }, + }, + { + name: "nil dst", + dst: nil, + src: map[string]interface{}{ + "foo": "bar", + }, + expected: map[string]interface{}{ + "foo": "bar", + }, + }, +} + +func TestPatchStruct(t *testing.T) { + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require := require.New(t) + dst, src := mustStruct(tc.dst), mustStruct(tc.src) + dstOrig, srcOrig := mustStruct(tc.dst), mustStruct(tc.src) + if tc.dst == nil { + dst = nil + dstOrig = nil + } + if tc.src == nil { + src = nil + srcOrig = nil + } + + actual := PatchStruct(dst, src) + require.Equal(mustStruct(tc.expected), actual) + require.Equal(dstOrig, dst) + require.Equal(srcOrig, src) + }) + } +} + +func TestPatchBytes(t *testing.T) { + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require := require.New(t) + dst, src := mustMarshal(tc.dst), mustMarshal(tc.src) + + actual, err := PatchBytes(dst, src) + require.NoError(err) + requireEqualEncoded(t, mustMarshal(tc.expected), actual) + }) + } +} + +func TestPatchBytesErr(t *testing.T) { + t.Run("dst", func(t *testing.T) { + require := require.New(t) + _, err := PatchBytes([]byte("foo"), nil) + require.EqualError(err, "error reading destination data: proto: cannot parse invalid wire-format data") + }) + t.Run("src", func(t *testing.T) { + require := require.New(t) + _, err := PatchBytes(nil, []byte("foo")) + require.EqualError(err, "error reading source data: proto: cannot parse invalid wire-format data") + }) +} + +func mustStruct(in map[string]interface{}) *structpb.Struct { + out, err := structpb.NewStruct(in) + if err != nil { + panic(err) + } + + return out +} + +func mustMarshal(in map[string]interface{}) []byte { + b, err := proto.Marshal(mustStruct(in)) + if err != nil { + panic(err) + } + + return b +} + +func requireEqualEncoded(t *testing.T, expected, actual []byte) { + t.Helper() + require := require.New(t) + + expectedpb, actualpb := new(structpb.Struct), new(structpb.Struct) + + err := proto.Unmarshal(expected, expectedpb) + require.NoError(err) + + err = proto.Unmarshal(actual, actualpb) + require.NoError(err) + + require.Equal(expectedpb, actualpb) +} From b64761f38fad5843c21d7fd24ef107df5ae99e28 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 28 Oct 2021 10:09:43 -0400 Subject: [PATCH 40/71] Add repository and functions around plugin host set membership (#1629) Co-authored-by: Todd --- go.mod | 3 +- go.sum | 3 +- internal/db/option.go | 11 + internal/db/option_test.go | 11 +- internal/db/read_writer.go | 15 +- .../oss/postgres/19/05_plugin_host.up.sql | 75 ++- internal/errors/code.go | 4 + internal/errors/code_test.go | 5 + internal/errors/info.go | 4 + internal/host/plugin/host.go | 39 +- internal/host/plugin/host_set_member.go | 60 +++ internal/host/plugin/host_set_member_test.go | 204 +++++++++ internal/host/plugin/host_test.go | 9 +- internal/host/plugin/options.go | 9 + internal/host/plugin/options_test.go | 6 + internal/host/plugin/repository_host.go | 433 +++++++++++------- internal/host/plugin/repository_host_set.go | 2 +- internal/host/plugin/repository_host_test.go | 33 +- internal/host/plugin/repository_host_util.go | 262 +++++++++++ .../host/plugin/repository_host_util_test.go | 312 +++++++++++++ internal/host/plugin/testing.go | 30 +- .../local/plugin/v1/host_plugin_service.proto | 12 +- sdk/pbs/plugin/host_plugin_service.pb.go | 136 +++--- 23 files changed, 1392 insertions(+), 286 deletions(-) create mode 100644 internal/host/plugin/host_set_member.go create mode 100644 internal/host/plugin/host_set_member_test.go create mode 100644 internal/host/plugin/repository_host_util.go create mode 100644 internal/host/plugin/repository_host_util_test.go diff --git a/go.mod b/go.mod index 5a0d7310f..2370513b1 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,8 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-rootcerts v1.0.2 - github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect + github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 github.com/hashicorp/go-secure-stdlib/kv-builder v0.1.1 diff --git a/go.sum b/go.sum index 9ddae006c..ad5afde4e 100644 --- a/go.sum +++ b/go.sum @@ -523,8 +523,9 @@ github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9j github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 h1:aCPwC2WPyomaw5gk5ZZ7DPevN8d9DS3igkmq6DxGMjU= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= -github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 h1:6KMBnfEv0/kLAz0O76sliN5mXbCDcLfs2kP7ssP7+DQ= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7mduW2Q+2FMrXawkLM8e7NU= github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ= github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 h1:9um9R8i0+HbRHS9d64kdvWR0/LJvo12sIonvR9zr1+U= diff --git a/internal/db/option.go b/internal/db/option.go index ba963875d..981a01a79 100644 --- a/internal/db/option.go +++ b/internal/db/option.go @@ -48,6 +48,10 @@ type Options struct { withGormFormatter hclog.Logger withMaxOpenConnections int + // withDebug indicates that the given operation should invoke Gorm's debug + // mode + withDebug bool + withOnConflict *OnConflict withRowsAffected *int64 } @@ -189,6 +193,13 @@ func WithMaxOpenConnections(max int) Option { } } +// WithDebug specifies the given operation should invoke debug mode in Gorm +func WithDebug(with bool) Option { + return func(o *Options) { + o.withDebug = with + } +} + // WithOnConflict specifies an optional on conflict criteria which specify // alternative actions to take when an insert results in a unique constraint or // exclusion constraint error diff --git a/internal/db/option_test.go b/internal/db/option_test.go index f2cf9588b..24e7159c1 100644 --- a/internal/db/option_test.go +++ b/internal/db/option_test.go @@ -192,13 +192,22 @@ func Test_getOpts(t *testing.T) { testOpts.withMaxOpenConnections = 22 assert.Equal(opts, testOpts) }) + t.Run("WithDebug", func(t *testing.T) { + assert := assert.New(t) + // test default of false + opts := GetOpts() + testOpts := getDefaultOptions() + assert.Equal(opts, testOpts) + // try setting to true + opts = GetOpts(WithDebug(true)) + testOpts.withDebug = true + }) t.Run("WithOnConflict", func(t *testing.T) { assert := assert.New(t) // test default of false opts := GetOpts() testOpts := getDefaultOptions() assert.Equal(opts, testOpts) - columns := SetColumns([]string{"name", "description"}) columnValues := SetColumnValues(map[string]interface{}{"expiration": "NULL"}) testOnConflict := OnConflict{ diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index e942147ae..749d5eb85 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -86,14 +86,14 @@ type Writer interface { // rows updated or an error. Supported options: WithOplog. Update(ctx context.Context, i interface{}, fieldMaskPaths []string, setToNullPaths []string, opt ...Option) (int, error) - // Create an object in the db with options: WithOplog - // the caller is responsible for the transaction life cycle of the writer + // Create an object in the db with options: WithOplog, WithOnConflict. + // The caller is responsible for the transaction life cycle of the writer // and if an error is returned the caller must decide what to do with // the transaction, which almost always should be to rollback. Create(ctx context.Context, i interface{}, opt ...Option) error // CreateItems will create multiple items of the same type. - // Supported options: WithOplog and WithOplogMsgs. WithOplog and + // Supported options: WithOplog and WithOplogMsgs. WithOplog and // WithOplogMsgs may not be used together. WithLookup is not a // supported option. The caller is responsible for the transaction life // cycle of the writer and if an error is returned the caller must decide @@ -101,15 +101,15 @@ type Writer interface { // rollback. CreateItems(ctx context.Context, createItems []interface{}, opt ...Option) error - // Delete an object in the db with options: WithOplog - // the caller is responsible for the transaction life cycle of the writer + // Delete an object in the db with options: WithOplog, WithDebug. + // The caller is responsible for the transaction life cycle of the writer // and if an error is returned the caller must decide what to do with // the transaction, which almost always should be to rollback. Delete // returns the number of rows deleted or an error. Delete(ctx context.Context, i interface{}, opt ...Option) (int, error) // DeleteItems will delete multiple items of the same type. - // Supported options: WithOplog and WithOplogMsgs. WithOplog and + // Supported options: WithOplog and WithOplogMsgs. WithOplog and // WithOplogMsgs may not be used together. The caller is responsible for the // transaction life cycle of the writer and if an error is returned the // caller must decide what to do with the transaction, which almost always @@ -664,6 +664,9 @@ func (rw *Db) Delete(ctx context.Context, i interface{}, opt ...Option) (int, er if opts.withWhereClause != "" { db = db.Where(opts.withWhereClause, opts.withWhereClauseArgs...) } + if opts.withDebug { + db = db.Debug() + } db = db.Delete(i) if db.Error != nil { return NoRowsAffected, errors.Wrap(ctx, db.Error, op, errors.WithoutEvent()) diff --git a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql index cc68eb046..fa17b888b 100644 --- a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql @@ -178,8 +178,6 @@ begin; references host (catalog_id, public_id) on delete cascade on update cascade, - constraint host_plugin_host_catalog_id_name_uq - unique(catalog_id, name), constraint host_plugin_host_catalog_id_external_id_uq unique(catalog_id, external_id), constraint host_plugin_host_catalog_id_public_id_uq @@ -195,8 +193,29 @@ begin; create trigger immutable_columns before update on host_plugin_host for each row execute procedure immutable_columns('public_id', 'catalog_id', 'external_id', 'create_time'); - create trigger insert_host_subtype before insert on host_plugin_host - for each row execute procedure insert_host_subtype(); + -- insert_host_plugin_host_subtype is intended as a before insert trigger on + -- host_plugin_host. Its purpose is to insert a base host for new plugin-based + -- hosts. It's a bit different than the standard trigger for this, because it + -- will have conflicting PKs and we just want to "do nothing" on those + -- conflicts, deferring the raising on an error to insert into the + -- host_plugin_host table. This allows the upsert-style workflow. + create or replace function + insert_host_plugin_host_subtype() + returns trigger + as $$ + begin + insert into host + (public_id, catalog_id) + values + (new.public_id, new.catalog_id) + on conflict do nothing; + + return new; + end; + $$ language plpgsql; + + create trigger insert_host_plugin_host_subtype before insert on host_plugin_host + for each row execute procedure insert_host_plugin_host_subtype(); create trigger delete_host_subtype after delete on host_plugin_host for each row execute procedure delete_host_subtype(); @@ -309,40 +328,19 @@ begin; create trigger insert_host_plugin_set_member before insert on host_plugin_set_member for each row execute procedure insert_host_plugin_set_member(); - -- delete_orphaned_host_plugin_host is an after delete trigger - -- function to delete plugin hosts when no more set members - -- reference it. - create function delete_orphaned_host_plugin_host() - returns trigger - as $$ - begin - delete from host_plugin_host - where not exists (select host_id - from host_plugin_set_member - where host_id = old.host_id) - and - public_id = old.host_id; - return null; - end; - $$ language plpgsql; - comment on function delete_orphaned_host_plugin_host is - 'delete_orphaned_host_plugin_host deletes a host when all set members with that host are deleted.'; - - create trigger delete_orphaned_host_plugin_host after delete on host_plugin_set_member - for each row execute procedure delete_orphaned_host_plugin_host(); - insert into oplog_ticket (name, version) values ('host_plugin_catalog', 1), ('host_plugin_catalog_secret', 1), - ('host_plugin_set', 1); + ('host_plugin_set', 1), + ('host_plugin_host', 1); - -- host_plugin_host_with_value_obj is useful for reading a plugin host with - -- its associated value objects (ip addresses, dns names, set membership) as - -- columns with delimited values. The delimiter depends on the value objects - -- (e.g. if they need ordering). - create view host_plugin_host_with_value_obj as + -- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a + -- plugin host with its associated value objects (ip addresses, dns names) and + -- set membership as columns with delimited values. The delimiter depends on + -- the value objects (e.g. if they need ordering). + create view host_plugin_host_with_value_obj_and_set_memberships as select h.public_id, h.catalog_id, @@ -354,15 +352,16 @@ begin; h.update_time, -- the string_agg(..) column will be null if there are no associated value objects string_agg(distinct host(hip.address), '|') as ip_addresses, - string_agg(distinct hdns.name, '|') as dns_names + string_agg(distinct hdns.name, '|') as dns_names, + string_agg(distinct hpsm.set_id, '|') as set_ids from host_plugin_host h - join host_plugin_catalog hc on h.catalog_id = hc.public_id - left outer join host_ip_address hip on h.public_id = hip.host_id - left outer join host_dns_name hdns on h.public_id = hdns.host_id - -- FIXME: add set membership once that's shaken out + join host_plugin_catalog hc on h.catalog_id = hc.public_id + left outer join host_ip_address hip on h.public_id = hip.host_id + left outer join host_dns_name hdns on h.public_id = hdns.host_id + left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id group by h.public_id, hc.plugin_id; - comment on view host_plugin_host_with_value_obj is + comment on view host_plugin_host_with_value_obj_and_set_memberships is 'host plugin host with its associated value objects'; commit; diff --git a/internal/errors/code.go b/internal/errors/code.go index 02ea814ec..1c4a75846 100644 --- a/internal/errors/code.go +++ b/internal/errors/code.go @@ -89,6 +89,10 @@ const ( // reasons related to the state of the DDL and/or inputs (such as we're // already in the right state and don't want to end up writing oplogs). GracefullyAborted Code = 1106 + // UnexpectedRowsAffected indicates that an action expected to operate on a + // specific number of records returned a different count, e.g. if you + // expected to delete three items and only one was deleted. + UnexpectedRowsAffected Code = 1107 // Migration setup errors are codes 2000-2999 MigrationIntegrity Code = 2000 // MigrationIntegrity represents an error with the generated migration related code diff --git a/internal/errors/code_test.go b/internal/errors/code_test.go index 0b491f66c..78326f4de 100644 --- a/internal/errors/code_test.go +++ b/internal/errors/code_test.go @@ -292,6 +292,11 @@ func TestCode_Both_String_Info(t *testing.T) { c: GracefullyAborted, want: GracefullyAborted, }, + { + name: "UnexpectedRowsAffected", + c: UnexpectedRowsAffected, + want: UnexpectedRowsAffected, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/errors/info.go b/internal/errors/info.go index c9ec7a77c..40004249d 100644 --- a/internal/errors/info.go +++ b/internal/errors/info.go @@ -236,4 +236,8 @@ var errorCodeInfo = map[Code]Info{ Message: "purposefully aborted without error", Kind: Other, }, + UnexpectedRowsAffected: { + Message: "unexpected number of rows affected", + Kind: Integrity, + }, } diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go index 9cccc4839..2da402973 100644 --- a/internal/host/plugin/host.go +++ b/internal/host/plugin/host.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/oplog" "google.golang.org/protobuf/proto" ) @@ -16,14 +17,15 @@ import ( // field for this host's host catalog. type Host struct { *store.Host - PluginId string `gorm:"-"` - tableName string `gorm:"-"` + PluginId string `gorm:"-"` + SetIds []string `gorm:"-"` + tableName string `gorm:"-"` } // newHost creates a new in memory Host assigned to catalogId with an address. // Supported options: WithName, WithDescription, WithIpAddresses, WithDnsNames, // WithPluginId. Others ignored. -func newHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host { +func NewHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host { opts := getOpts(opt...) h := &Host{ @@ -74,10 +76,31 @@ func allocHost() *Host { func (h *Host) clone() *Host { cp := proto.Clone(h.Host) - return &Host{ + nh := &Host{ PluginId: h.PluginId, Host: cp.(*store.Host), } + switch { + case h.SetIds == nil: + case len(h.SetIds) == 0: + nh.SetIds = make([]string, 0) + default: + nh.SetIds = make([]string, len(h.SetIds)) + copy(nh.SetIds, h.SetIds) + } + return nh +} + +func (h *Host) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{h.PublicId}, + "resource-type": []string{"plugin-host"}, + "op-type": []string{op.String()}, + } + if h.CatalogId != "" { + metadata["catalog-id"] = []string{h.CatalogId} + } + return metadata } // hostAgg is a view that aggregates the host's value objects in to @@ -93,6 +116,7 @@ type hostAgg struct { UpdateTime *timestamp.Timestamp IpAddresses string DnsNames string + SetIds string } func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { @@ -118,12 +142,17 @@ func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { sort.Strings(h.DnsNames) } + if agg.SetIds != "" { + h.SetIds = strings.Split(agg.SetIds, aggregateDelimiter) + sort.Strings(h.SetIds) + } + return h, nil } // TableName returns the table name for gorm func (agg *hostAgg) TableName() string { - return "host_plugin_host_with_value_obj" + return "host_plugin_host_with_value_obj_and_set_memberships" } func (agg *hostAgg) GetPublicId() string { diff --git a/internal/host/plugin/host_set_member.go b/internal/host/plugin/host_set_member.go new file mode 100644 index 000000000..912f148df --- /dev/null +++ b/internal/host/plugin/host_set_member.go @@ -0,0 +1,60 @@ +package plugin + +import ( + "context" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host/plugin/store" +) + +// A HostSetMember represents the membership of a host in a host set. +type HostSetMember struct { + *store.HostSetMember + tableName string `gorm:"-"` +} + +// NewHostSetMember creates a new in memory HostSetMember representing the +// membership of hostId in hostSetId. +func NewHostSetMember(ctx context.Context, setId, hostId string, opt ...Option) (*HostSetMember, error) { + const op = "plugin.NewHostSetMember" + if setId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no set id") + } + if hostId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no host id") + } + member := &HostSetMember{ + HostSetMember: &store.HostSetMember{ + SetId: setId, + HostId: hostId, + }, + } + return member, nil +} + +// VetForWrite implements db.VetForWrite() interface for host set members. +func (m *HostSetMember) VetForWrite(ctx context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error { + const op = "plugin.(HostSetMember).VetForWrite" + if m.SetId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing set id") + } + if m.HostId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing host id") + } + return nil +} + +// TableName returns the table name for the host set. +func (m *HostSetMember) TableName() string { + if m.tableName != "" { + return m.tableName + } + return "host_plugin_set_member" +} + +// SetTableName sets the table name. If the caller attempts to +// set the name to "" the name will be reset to the default name. +func (m *HostSetMember) SetTableName(n string) { + m.tableName = n +} diff --git a/internal/host/plugin/host_set_member_test.go b/internal/host/plugin/host_set_member_test.go new file mode 100644 index 000000000..47c67844e --- /dev/null +++ b/internal/host/plugin/host_set_member_test.go @@ -0,0 +1,204 @@ +package plugin + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host/plugin/store" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestHostSetMember_InsertDelete tests insertion and validates the lookup via set IDs +// function as well. After, we remove set memberships one at a time and validate +// the cleanup function removes the hosts. +func TestHostSetMember_InsertDelete(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + plg := hostplg.TestPlugin(t, conn, "create") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), + } + + cats := TestCatalogs(t, conn, prj.PublicId, plg.PublicId, 2) + + blueCat := cats[0] + blueSet1 := TestSet(t, conn, kms, blueCat, plgm) + blueSet2 := TestSet(t, conn, kms, blueCat, plgm) + + hostId, err := db.NewPublicId(HostPrefix) + require.NoError(t, err) + blueHost1 := NewHost(ctx, blueCat.PublicId, "abcd", withPluginId(plg.GetPublicId())) + blueHost1.PublicId = hostId + require.NoError(t, rw.Create(ctx, blueHost1)) + + hostId, err = db.NewPublicId(HostPrefix) + require.NoError(t, err) + blueHost2 := NewHost(ctx, blueCat.PublicId, "zyxw", withPluginId(plg.GetPublicId())) + blueHost2.PublicId = hostId + require.NoError(t, rw.Create(ctx, blueHost2)) + + greenCat := cats[1] + greenSet := TestSet(t, conn, kms, greenCat, plgm) + + tests := []struct { + name string + sets []string + host *Host + wantErr bool + direct bool + }{ + { + name: "valid-host-in-set", + sets: []string{blueSet1.PublicId}, + host: blueHost1, + }, + { + name: "valid-other-host-in-set", + sets: []string{blueSet2.PublicId}, + host: blueHost2, + }, + { + name: "invalid-diff-catalogs", + sets: []string{greenSet.PublicId}, + host: blueHost1, + wantErr: true, + }, + { + name: "test-vet-for-write-no-set", + host: blueHost1, + sets: []string{""}, + wantErr: true, + direct: true, + }, + { + name: "test-vet-for-write-no-host", + sets: []string{blueSet1.PublicId}, + wantErr: true, + direct: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + for _, set := range tt.sets { + var got *HostSetMember + var err error + if !tt.direct { + got, err = NewHostSetMember(ctx, set, tt.host.PublicId) + } else { + got = &HostSetMember{ + HostSetMember: &store.HostSetMember{ + SetId: set, + }, + } + if tt.host != nil { + got.HostId = tt.host.PublicId + } + } + require.NoError(err) + require.NotNil(got) + err2 := rw.Create(ctx, got) + if tt.wantErr { + assert.Error(err2) + return + } + assert.NoError(err2) + } + + // Run a test on the aggregate to validate looking up sets + agg := &hostAgg{PublicId: tt.host.PublicId} + require.NoError(rw.LookupByPublicId(ctx, agg)) + h, err := agg.toHost(ctx) + require.NoError(err) + assert.ElementsMatch(h.SetIds, tt.sets) + }) + } + repo, err := NewRepository(rw, rw, kms, plgm) + require.NoError(t, err) + hosts, err := repo.ListHostsBySetIds(ctx, []string{blueSet1.PublicId, blueSet2.PublicId}) + require.NoError(t, err) + require.Len(t, hosts, 2) + + // Base case the count by catalog ID + hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + require.NoError(t, err) + assert.Len(t, hosts, 2) + + // Delete first membership, validate host is gone + got, err := NewHostSetMember(ctx, blueSet1.PublicId, blueHost1.PublicId) + require.NoError(t, err) + require.NotNil(t, got) + num, err := rw.Delete(ctx, got) + require.NoError(t, err) + assert.Equal(t, 1, num) + require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost1.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) + hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + require.NoError(t, err) + require.Len(t, hosts, 1) + + // Delete second, validate second host is gone + got, err = NewHostSetMember(ctx, blueSet2.PublicId, blueHost2.PublicId) + require.NoError(t, err) + require.NotNil(t, got) + num, err = rw.Delete(ctx, got) + require.NoError(t, err) + assert.Equal(t, 1, num) + require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost2.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) + hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + require.NoError(t, err) + require.Len(t, hosts, 0) +} + +func TestHostSetMember_SetTableName(t *testing.T) { + defaultTableName := "host_plugin_set_member" + tests := []struct { + name string + initialName string + setNameTo string + want string + }{ + { + name: "new-name", + initialName: "", + setNameTo: "new-name", + want: "new-name", + }, + { + name: "reset to default", + initialName: "initial", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + def := &HostSetMember{ + HostSetMember: &store.HostSetMember{}, + } + require.Equal(defaultTableName, def.TableName()) + s := &HostSetMember{ + HostSetMember: &store.HostSetMember{}, + tableName: tt.initialName, + } + s.SetTableName(tt.setNameTo) + assert.Equal(tt.want, s.TableName()) + }) + } +} diff --git a/internal/host/plugin/host_test.go b/internal/host/plugin/host_test.go index b82a5c3f8..3dcbcb2c1 100644 --- a/internal/host/plugin/host_test.go +++ b/internal/host/plugin/host_test.go @@ -84,7 +84,7 @@ func TestHost_Create(t *testing.T) { }, }, { - name: "duplicate-name", + name: "valid-duplicate-name-same-catalog", args: args{ catalogId: cat.GetPublicId(), externalId: "duplicate-name", @@ -99,7 +99,6 @@ func TestHost_Create(t *testing.T) { Name: "test-name", }, }, - wantErr: true, }, { name: "valid-duplicate-name-different-catalog", @@ -141,7 +140,7 @@ func TestHost_Create(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - got := newHost(ctx, tt.args.catalogId, tt.args.externalId, tt.args.opts...) + got := NewHost(ctx, tt.args.catalogId, tt.args.externalId, tt.args.opts...) require.NotNil(t, got) assert.Emptyf(t, got.PublicId, "PublicId set") assert.Equal(t, tt.want, got) @@ -208,10 +207,10 @@ func testHost(t *testing.T, conn *db.DB, catId, externId string) *Host { t.Helper() w := db.New(conn) ctx := context.Background() - host1 := newHost(ctx, catId, externId) + host1 := NewHost(ctx, catId, externId) var err error host1.PublicId, err = newHostId(ctx, catId, externId) require.NoError(t, err) require.NoError(t, w.Create(ctx, host1)) return host1 -} \ No newline at end of file +} diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index 0467bfa82..9829982f6 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -25,6 +25,7 @@ type options struct { withIpAddresses []string withDnsNames []string withLimit int + withSetIds []string } func getDefaultOptions() options { @@ -97,3 +98,11 @@ func WithLimit(l int) Option { o.withLimit = l } } + +// WithSetIds provides the ability to restrict lookups to particular matching +// sets. +func WithSetIds(with []string) Option { + return func(o *options) { + o.withSetIds = with + } +} diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index 758e3d619..a8c4b2b3f 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -50,4 +50,10 @@ func Test_GetOpts(t *testing.T) { testOpts.withIpAddresses = []string{"foo"} assert.EqualValues(t, opts, testOpts) }) + t.Run("withSetIds", func(t *testing.T) { + opts := getOpts(WithSetIds([]string{"foo"})) + testOpts := getDefaultOptions() + testOpts.withSetIds = []string{"foo"} + assert.EqualValues(t, opts, testOpts) + }) } diff --git a/internal/host/plugin/repository_host.go b/internal/host/plugin/repository_host.go index be6cc4214..938d6c6a9 100644 --- a/internal/host/plugin/repository_host.go +++ b/internal/host/plugin/repository_host.go @@ -3,11 +3,9 @@ package plugin import ( "context" "fmt" - "sort" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" @@ -25,7 +23,7 @@ import ( func (r *Repository) UpsertHosts( ctx context.Context, hc *HostCatalog, - sets []string, + setIds []string, phs []*plgpb.ListHostsResponseHost, _ ...Option) ([]*Host, error) { const op = "plugin.(Repository).UpsertHosts" @@ -46,182 +44,204 @@ func (r *Repository) UpsertHosts( if hc.GetScopeId() == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } - if sets == nil { + if setIds == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "nil sets") } - if len(sets) == 0 { // At least one must have been given to the plugin + if len(setIds) == 0 { // At least one must have been given to the plugin return nil, errors.New(ctx, errors.InvalidParameter, op, "empty sets") } - // hostInfo stores the info we need for the transaction below as well as - // which sets they matched - type hostInfo struct { - h *Host - ips []interface{} - dnsNames []interface{} - sets map[string]struct{} - } - hostMapping := make(map[string]hostInfo, len(phs)) - var err error - var totalMsgs uint32 - - for _, ph := range phs { - totalMsgs += 2 // delete and create - - h := newHost(ctx, - hc.GetPublicId(), - ph.GetExternalId(), - withIpAddresses(ph.GetIpAddresses()), - withDnsNames(ph.GetDnsNames()), - withPluginId(hc.GetPluginId())) - h.PublicId, err = newHostId(ctx, hc.GetPublicId(), ph.GetExternalId()) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - - var ipAddresses []interface{} - if len(h.GetIpAddresses()) > 0 { - sort.Strings(h.IpAddresses) - ipAddresses = make([]interface{}, 0, len(h.GetIpAddresses())) - for _, a := range h.GetIpAddresses() { - obj, err := host.NewIpAddress(ctx, h.PublicId, a) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - ipAddresses = append(ipAddresses, obj) - } - } - - var dnsNames []interface{} - if len(h.GetDnsNames()) > 0 { - sort.Strings(h.DnsNames) - dnsNames = make([]interface{}, 0, len(h.GetDnsNames())) - for _, n := range h.GetDnsNames() { - obj, err := host.NewDnsName(ctx, h.PublicId, n) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - dnsNames = append(dnsNames, obj) - } - } - - hi := hostInfo{ - h: h, - ips: ipAddresses, - dnsNames: dnsNames, - } - - for _, id := range ph.GetSetIds() { - if hi.sets == nil { - hi.sets = make(map[string]struct{}) - } - hi.sets[id] = struct{}{} - } - - hostMapping[h.PublicId] = hi - - totalMsgs += uint32(len(ipAddresses) + len(dnsNames)) - } - oplogWrapper, err := r.kms.GetWrapper(ctx, hc.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } + // First, fetch existing hosts for the set IDs passed in, and organize them + // into a lookup map by host ID for later usage + var currentHosts []*Host + var currentHostMap map[string]*Host + { + var err error + currentHosts, err = r.ListHostsBySetIds(ctx, setIds) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up current hosts for returned sets")) + } + + currentHostMap = make(map[string]*Host, len(currentHosts)) + for _, h := range currentHosts { + currentHostMap[h.PublicId] = h + } + } + + // Now, parse the externally defined hosts into hostInfo values, which + // stores info useful for later comparisons + newHostMap, err := createNewHostMap(ctx, hc, phs, currentHostMap) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create new host map")) + } + var returnedHosts []*Host + // Iterate over hosts and add or update them + for _, hi := range newHostMap { + ret := hi.h.clone() - // Here's what this function does: first it deletes a host, which causes - // cascading deletes on ip addresses and dns names (but all within the DB). - // Then the host is recreated, which uses the same public ID (since it's - // based on the host catalog/external ID), along with new/updated - // information. - // - // If we ever do start allowing hosts directly in targets this may cause a - // problem because the host deletion would probably cascade to the target - // and remove it from the target host sources, but at this point there are - // several issues with that scenario so it's not soon... - // - // The main reason I'm doing it this way is because I'm not sure how to do - // oplogs with custom queries. Otherwise a custom insert/on conflict query - // could replace the initial deletion. @jimlambrt @mgaffney help? :-D - _, err = r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(r db.Reader, w db.Writer) error { - // FIXME: We need to ensure hosts no longer found have their - // memberships removed. To do this, first list all set memberships. - // This is waiting on membership to be plumbed through to gorm, - // which in turn is waiting on figuring out whether or not we can - // share that between static and plugin. - // - // Next, for each set, check whether a given host ID is in the - // hostMapping and if so, if the set ID is in the host's sets. If - // not, queue that membership entry for deletion. - msgs := make([]*oplog.Message, 0, totalMsgs+3) - ticket, err := w.GetTicket(hc) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) - } - - for _, hi := range hostMapping { - ret := hi.h.clone() - - var hOplogMsg oplog.Message - // We don't need to check whether something was deleted because - // it's okay if it fails - if _, err := w.Delete(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil { - return errors.Wrap(ctx, err, op) + if !hi.dirtyHost && + len(hi.ipsToAdd) == 0 && + len(hi.ipsToRemove) == 0 && + len(hi.dnsNamesToAdd) == 0 && + len(hi.dnsNamesToRemove) == 0 { + returnedHosts = append(returnedHosts, ret) + continue + } + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0) + ticket, err := w.GetTicket(hi.h) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) } - msgs = append(msgs, &hOplogMsg) - hOplogMsg = oplog.Message{} - if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil { - return errors.Wrap(ctx, err, op) - } - msgs = append(msgs, &hOplogMsg) - - if len(hi.ips) > 0 { - ipOplogMsgs := make([]*oplog.Message, 0, len(hi.ips)) - if err := w.CreateItems(ctx, hi.ips, db.NewOplogMsgs(&ipOplogMsgs)); err != nil { - return err + if hi.dirtyHost { + var hOplogMsg oplog.Message + onConflict := &db.OnConflict{ + Target: db.Constraint("host_plugin_host_pkey"), + Action: db.SetColumns([]string{"name", "description"}), } - msgs = append(msgs, ipOplogMsgs...) - } - - if len(hi.dnsNames) > 0 { - dnsOplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNames)) - if err := w.CreateItems(ctx, hi.dnsNames, db.NewOplogMsgs(&dnsOplogMsgs)); err != nil { - return err + if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg), db.WithOnConflict(onConflict)); err != nil { + return errors.Wrap(ctx, err, op) } - msgs = append(msgs, dnsOplogMsgs...) + msgs = append(msgs, &hOplogMsg) } - // FIXME: Add set memberships here + // IP handling + { + if len(hi.ipsToRemove) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToRemove)) + count, err := w.DeleteItems(ctx, hi.ipsToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) + if err != nil { + return err + } + if count != len(hi.ipsToRemove) { + return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d ips from host %s, removed %d", len(hi.ipsToRemove), hi.h.PublicId, count)) + } + msgs = append(msgs, oplogMsgs...) + } + if len(hi.ipsToAdd) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToAdd)) + if err := w.CreateItems(ctx, hi.ipsToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { + return err + } + msgs = append(msgs, oplogMsgs...) + } + } + + // DNS handling + { + if len(hi.dnsNamesToRemove) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToRemove)) + count, err := w.DeleteItems(ctx, hi.dnsNamesToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) + if err != nil { + return err + } + if count != len(hi.dnsNamesToRemove) { + return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d dns names from host %s, removed %d", len(hi.dnsNamesToRemove), hi.h.PublicId, count)) + } + msgs = append(msgs, oplogMsgs...) + } + if len(hi.dnsNamesToAdd) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToAdd)) + if err := w.CreateItems(ctx, hi.dnsNamesToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { + return err + } + msgs = append(msgs, oplogMsgs...) + } + } + + metadata := hi.h.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } returnedHosts = append(returnedHosts, ret) - } - - metadata := hc.oplog(oplog.OpType_OP_TYPE_CREATE) - if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) - } - - return nil - }, - ) - - if err != nil { - if errors.IsCheckConstraintError(err) || errors.IsNotNullError(err) { - return nil, errors.New(ctx, - errors.InvalidAddress, - op, - fmt.Sprintf("in catalog: %s", hc.GetPublicId()), - errors.WithWrap(err), - ) + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op) } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", hc.PublicId))) } + + // Now, check set membership changes + setMembershipsToAdd, setMembershipsToRemove, allSetIds := getSetChanges(currentHostMap, newHostMap) + + // Iterate through the sets and update memberships, one transaction per set + for setId := range allSetIds { + hs, err := NewHostSet(ctx, hc.PublicId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hs.PublicId = setId + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0) + + ticket, err := w.GetTicket(hs) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + // Perform additions + for _, hostId := range setMembershipsToAdd[hs.PublicId] { + membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + var hOplogMsg oplog.Message + if err := w.Create(ctx, membership, db.NewOplogMsg(&hOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &hOplogMsg) + } + + // Perform removals + for _, hostId := range setMembershipsToRemove[hs.PublicId] { + membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + var hOplogMsg oplog.Message + rows, err := w.Delete(ctx, membership, db.NewOplogMsg(&hOplogMsg)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rows != 1 { + return errors.New(ctx, errors.RecordNotFound, op, "record not found when deleting set membership") + } + msgs = append(msgs, &hOplogMsg) + } + + metadata := hc.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + } + return returnedHosts, nil } @@ -248,10 +268,10 @@ func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Opt return h, nil } -// ListHosts returns a slice of Hosts for the catalogId. +// ListHostsByCatalogId returns a slice of Hosts for the catalogId. // WithLimit is the only option supported. -func (r *Repository) ListHosts(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) { - const op = "plugin.(Repository).ListHosts" +func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) { + const op = "plugin.(Repository).ListHostsByCatalogId" if catalogId == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") } @@ -282,3 +302,96 @@ func (r *Repository) ListHosts(ctx context.Context, catalogId string, opt ...Opt return hosts, nil } + +// ListHostsBySetId returns a slice of Hosts for the given set IDs. +// WithLimit is the only option supported. +func (r *Repository) ListHostsBySetIds(ctx context.Context, setIds []string, opt ...Option) ([]*Host, error) { + const op = "plugin.(Repository).ListHostsBySetIds" + if len(setIds) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no set ids") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + + query := ` +public_id in + (select distinct host_id + from host_plugin_set_member + where set_id in (?)) +` + + var hostAggs []*hostAgg + err := r.reader.SearchWhere(ctx, &hostAggs, query, []interface{}{setIds}, db.WithLimit(limit)) + + switch { + case err != nil: + return nil, errors.Wrap(ctx, err, op) + case hostAggs == nil: + return nil, nil + } + + hosts := make([]*Host, 0, len(hostAggs)) + for _, ha := range hostAggs { + host, err := ha.toHost(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hosts = append(hosts, host) + } + + return hosts, nil +} + +// DeleteOphanedHosts deletes any hosts that no longer belong to any set. +// WithLimit is the only option supported. No options are currently supported. +func (r *Repository) DeleteOrphanedHosts(ctx context.Context, scopeId string, _ ...Option) error { + const op = "plugin.(Repository).DeleteOrphanedHosts" + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + query := ` +public_id in + (select public_id + from host_plugin_host + where public_id not in + (select host_id from host_plugin_set_member)) +` + + var hostAggs []*hostAgg + err = r.reader.SearchWhere(ctx, &hostAggs, query, nil) + switch { + case err != nil: + return errors.Wrap(ctx, err, op) + case len(hostAggs) == 0: + return nil + } + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + for _, ha := range hostAggs { + h := NewHost(ctx, ha.CatalogId, ha.ExternalId) + h.PublicId = ha.PublicId + metadata := h.oplog(oplog.OpType_OP_TYPE_DELETE) + dHost := h.clone() + if _, err := w.Delete(ctx, dHost, db.WithOplog(oplogWrapper, metadata)); err != nil { + return errors.Wrap(ctx, err, op) + } + } + return nil + }) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index b89821dce..96b1acf40 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -648,7 +648,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En if _, ok := hostIds[hostId]; !ok { hostIds[hostId] = true - host := newHost(ctx, ci.publicId, addr) + host := NewHost(ctx, ci.publicId, addr) host.PublicId = hostId hosts = append(hosts, host) } diff --git a/internal/host/plugin/repository_host_test.go b/internal/host/plugin/repository_host_test.go index de59125a9..460192d0e 100644 --- a/internal/host/plugin/repository_host_test.go +++ b/internal/host/plugin/repository_host_test.go @@ -43,7 +43,10 @@ func TestRepository_UpsertHosts(t *testing.T) { set := TestSet(t, conn, kms, catalog, plgm) setIds = append(setIds, set.GetPublicId()) } + sort.Strings(setIds) phs, exp := TestExternalHosts(t, catalog, setIds, setCount) + phs[2].Name = phs[0].Name + exp[2].Name = exp[0].Name type input struct { catalog *HostCatalog @@ -139,7 +142,7 @@ func TestRepository_UpsertHosts(t *testing.T) { wantIsErr: errors.InvalidParameter, }, { - name: "valid", + name: "valid", // Note: this also tests duplicate names in: func() *input { return &input{ catalog: catalog, @@ -164,7 +167,11 @@ func TestRepository_UpsertHosts(t *testing.T) { sort.Strings(e.IpAddresses) sort.Strings(e.DnsNames) + ph.Name, ph.Description = ph.Description, ph.Name + e.Name, e.Description = e.Description, e.Name + ph.SetIds = ph.SetIds[0 : len(ph.SetIds)-1] + e.SetIds = e.SetIds[0 : len(e.SetIds)-1] return &input{ catalog: catalog, sets: setIds, @@ -194,7 +201,9 @@ func TestRepository_UpsertHosts(t *testing.T) { // Basic tests assert.Len(got, len(in.phs)) - assert.NoError(db.TestVerifyOplog(t, rw, in.catalog.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + for _, h := range in.exp { + assert.NoError(db.TestVerifyOplog(t, rw, h.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + } // Make sure outputs match. Ignore timestamps. assert.Empty( @@ -210,7 +219,7 @@ func TestRepository_UpsertHosts(t *testing.T) { ) // Check again, but via performing an explicit list - got, err = repo.ListHosts(ctx, in.catalog.GetPublicId()) + got, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId()) require.NoError(err) require.NotNil(got) assert.Len(got, len(in.phs)) @@ -226,11 +235,17 @@ func TestRepository_UpsertHosts(t *testing.T) { ), ) - // Now individually call read on each host + // Now individually call read on each host, cache the matching set + // IDs, and then check membership + setIdMap := make(map[string][]string) for _, exp := range in.exp { + for _, setId := range exp.SetIds { + setIdMap[setId] = append(setIdMap[setId], exp.GetPublicId()) + } got, err := repo.LookupHost(ctx, exp.GetPublicId()) require.NoError(err) require.NotNil(got) + assert.NotEmpty(got.SetIds) assert.Empty( cmp.Diff( exp, @@ -240,6 +255,16 @@ func TestRepository_UpsertHosts(t *testing.T) { ), ) } + for setId, expHostIds := range setIdMap { + got, err = repo.ListHostsBySetIds(ctx, []string{setId}) + require.NoError(err) + require.NotNil(got) + var gotHostIds []string + for _, h := range got { + gotHostIds = append(gotHostIds, h.GetPublicId()) + } + assert.ElementsMatch(expHostIds, gotHostIds) + } }) } } diff --git a/internal/host/plugin/repository_host_util.go b/internal/host/plugin/repository_host_util.go new file mode 100644 index 000000000..fb8cd97db --- /dev/null +++ b/internal/host/plugin/repository_host_util.go @@ -0,0 +1,262 @@ +package plugin + +import ( + "context" + "sort" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/host" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/hashicorp/go-secure-stdlib/strutil" +) + +// valueToInterfaceMap is a map that has a function to convert values into an +// array +type valueToInterfaceMap map[string]interface{} + +func (m valueToInterfaceMap) toArray() []interface{} { + switch { + case m == nil: + return nil + case len(m) == 0: + return make([]interface{}, 0) + default: + ret := make([]interface{}, 0, len(m)) + for _, v := range m { + ret = append(ret, v) + } + return ret + } +} + +// hostInfo stores the info we need for figuring out host, set membership, +// and value object differences. It also stores dirty flags to indicate +// whether we need to update value objects or the host itself. +type hostInfo struct { + h *Host + ipsToAdd valueToInterfaceMap + ipsToRemove valueToInterfaceMap + dnsNamesToAdd valueToInterfaceMap + dnsNamesToRemove valueToInterfaceMap + dirtyHost bool +} + +func createNewHostMap(ctx context.Context, + catalog *HostCatalog, + phs []*plgpb.ListHostsResponseHost, + currentHostMap map[string]*Host) (map[string]*hostInfo, error) { + + const op = "plugin.createNewHostMap" + newHostMap := make(map[string]*hostInfo, len(phs)) + + var err error + for _, ph := range phs { + newHost := NewHost(ctx, + catalog.GetPublicId(), + ph.GetExternalId(), + WithName(ph.GetName()), + WithDescription(ph.GetDescription()), + withIpAddresses(ph.GetIpAddresses()), + withDnsNames(ph.GetDnsNames()), + withPluginId(catalog.GetPluginId())) + newHost.PublicId, err = newHostId(ctx, catalog.GetPublicId(), ph.GetExternalId()) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + newHost.SetIds = ph.SetIds + hi := &hostInfo{ + h: newHost, + } + newHostMap[newHost.PublicId] = hi + + // Check if the host is dirty; that is, we need to perform an upsert + // operation. If the host isn't dirty, we have nothing to do. Note + // that we don't check every value exhaustively; for instance, we + // assume catalog ID and external ID don't change because if they do + // the public ID will be different as well. + currHost := currentHostMap[newHost.PublicId] + switch { + case currHost == nil, + currHost.Name != newHost.Name, + currHost.Description != newHost.Description: + hi.dirtyHost = true + } + + // Get the current set of host IPs/DNS names for comparison. These + // will be in sorted order since ordering is kept in the database + // and they will have been sorted before insertion. + var currHostIps []string + var currHostDnsNames []string + if currHost != nil { + currHostIps = currHost.IpAddresses + currHostDnsNames = currHost.DnsNames + } + + // Sort these here before comparison. We always use a priority order + // based on the behavior of sort.Strings so that we can check for + // equivalency. + sort.Strings(newHost.IpAddresses) + sort.Strings(newHost.DnsNames) + + // IPs + { + switch { + case strutil.EquivalentSlices(currHostIps, newHost.GetIpAddresses()): + // Nothing to do...don't remove or add anything + + default: + // No match, so we need to remove the old ones and add the new + + // First, build up removals + for _, ip := range currHostIps { + if hi.ipsToRemove == nil { + hi.ipsToRemove = make(valueToInterfaceMap, len(currHostIps)) + } + obj, err := host.NewIpAddress(ctx, newHost.PublicId, ip) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hi.ipsToRemove[ip] = obj + } + + // Now build up additions + for _, ip := range newHost.GetIpAddresses() { + // If it's in ipsToRemove we found it on the current host; + // finding it again here means the host still has it, so + // simply do no change. + if hi.ipsToRemove != nil && hi.ipsToRemove[ip] != nil { + delete(hi.ipsToRemove, ip) + if len(hi.ipsToRemove) == 0 { + hi.ipsToRemove = nil + } + continue + } + if hi.ipsToAdd == nil { + hi.ipsToAdd = make(valueToInterfaceMap, len(newHost.GetIpAddresses())) + } + obj, err := host.NewIpAddress(ctx, newHost.PublicId, ip) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hi.ipsToAdd[ip] = obj + } + } + } + + // DNS names + { + switch { + case strutil.EquivalentSlices(currHostDnsNames, newHost.GetDnsNames()): + // Nothing to do...don't remove or add anything + + default: + // No match, so we need to remove the old ones and add the new + + // First, build up removals + for _, name := range currHostDnsNames { + if hi.dnsNamesToRemove == nil { + hi.dnsNamesToRemove = make(valueToInterfaceMap, len(currHostDnsNames)) + } + obj, err := host.NewDnsName(ctx, newHost.PublicId, name) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hi.dnsNamesToRemove[name] = obj + } + + // Now build up additions + for _, name := range newHost.GetDnsNames() { + // If it's in dnsNamesToRemove we found it on the current + // host; finding it again here means the host still has it, + // so simply do no change. + if hi.dnsNamesToRemove != nil && hi.dnsNamesToRemove[name] != nil { + delete(hi.dnsNamesToRemove, name) + if len(hi.dnsNamesToRemove) == 0 { + hi.dnsNamesToRemove = nil + } + continue + } + if hi.dnsNamesToAdd == nil { + hi.dnsNamesToAdd = make(valueToInterfaceMap, len(newHost.GetDnsNames())) + } + obj, err := host.NewDnsName(ctx, newHost.PublicId, name) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hi.dnsNamesToAdd[name] = obj + } + } + } + } + + return newHostMap, nil +} + +func getSetChanges( + currentHostMap map[string]*Host, + newHostMap map[string]*hostInfo) ( + setMembershipsToAdd, setMembershipsToRemove map[string][]string, + allSetIds map[string]struct{}) { + + // First, find sets that hosts should be added to: hosts that are new or + // have new set IDs returned. + for newHostId, newHost := range newHostMap { + var setsToAdd []string + currentHost, ok := currentHostMap[newHostId] + if !ok { + // If the host was not known about before now, any sets the host + // matches will need to be added + setsToAdd = newHost.h.SetIds + } else { + // Otherwise, add to any it doesn't currently match + for _, setId := range newHost.h.SetIds { + if !strutil.StrListContains(currentHost.SetIds, setId) { + setsToAdd = append(setsToAdd, setId) + } + } + } + // Add to the total set + for _, setToAdd := range setsToAdd { + if setMembershipsToAdd == nil { + setMembershipsToAdd = make(map[string][]string) + } + if allSetIds == nil { + allSetIds = make(map[string]struct{}) + } + setMembershipsToAdd[setToAdd] = append(setMembershipsToAdd[setToAdd], newHostId) + allSetIds[setToAdd] = struct{}{} + } + } + + // Now, do the inverse: remove hosts from sets that appear there now but no + // longer have that set ID in their current list. + for currentHostId, currentHost := range currentHostMap { + var setsToRemove []string + newHost, ok := newHostMap[currentHostId] + if !ok { + // If the host doesn't even appear now, we obviously want to remove + // it from all existing set memberships + setsToRemove = currentHost.SetIds + } else { + // Otherwise, remove it from any it doesn't currently have + for _, setId := range currentHost.SetIds { + if !strutil.StrListContains(newHost.h.SetIds, setId) { + setsToRemove = append(setsToRemove, setId) + } + } + } + // Add to the total set + for _, setToRemove := range setsToRemove { + if setMembershipsToRemove == nil { + setMembershipsToRemove = make(map[string][]string) + } + if allSetIds == nil { + allSetIds = make(map[string]struct{}) + } + setMembershipsToRemove[setToRemove] = append(setMembershipsToRemove[setToRemove], currentHostId) + allSetIds[setToRemove] = struct{}{} + } + } + + return +} diff --git a/internal/host/plugin/repository_host_util_test.go b/internal/host/plugin/repository_host_util_test.go new file mode 100644 index 000000000..fe852492d --- /dev/null +++ b/internal/host/plugin/repository_host_util_test.go @@ -0,0 +1,312 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/boundary/internal/host" + hoststore "github.com/hashicorp/boundary/internal/host/store" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestUtilFunctions(t *testing.T) { + ctx := context.Background() + const externalId = "ext_1234567890" + const pluginId = "plg_1234567890" + + catalog, err := NewHostCatalog(ctx, "p_1234567890", pluginId) + require.NoError(t, err) + require.NotNil(t, catalog) + catalog.PublicId = "hc_1234567890" + baseHostId, err := newHostId(ctx, catalog.GetPublicId(), externalId) + require.NoError(t, err) + + baseResponseHost := &plgpb.ListHostsResponseHost{ + ExternalId: externalId, + Name: "base-name", + Description: "base-description", + IpAddresses: []string{"1.2.3.4", "5.6.7.8"}, + DnsNames: []string{"a.b.c", "x.y.z"}, + SetIds: []string{"set1", "set2"}, + } + baseIpsIfaces := valueToInterfaceMap{} + for _, v := range baseResponseHost.IpAddresses { + ip, err := host.NewIpAddress(ctx, baseHostId, v) + require.NoError(t, err) + baseIpsIfaces[v] = ip + } + baseDnsNamesIfaces := valueToInterfaceMap{} + for _, v := range baseResponseHost.DnsNames { + name, err := host.NewDnsName(ctx, baseHostId, v) + require.NoError(t, err) + baseDnsNamesIfaces[v] = name + } + baseHost := NewHost(ctx, catalog.PublicId, externalId) + baseHost.Name = baseResponseHost.Name + baseHost.Description = baseResponseHost.Description + baseHost.IpAddresses = baseResponseHost.IpAddresses + baseHost.DnsNames = baseResponseHost.DnsNames + baseHost.PluginId = pluginId + baseHost.SetIds = baseResponseHost.SetIds + + defaultHostFunc := func(in *Host) *Host { + return in + } + defaultSetsFunc := func() []string { + return baseHost.SetIds + } + + tests := []struct { + name string + host func(*Host) *Host + // This function should take in a base host (which will be provided as a + // clone) and transform however needed for the particular test + in func(*plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) + sets func() []string + setsToAdd map[string][]string + setsToRemove map[string][]string + }{ + { + name: "base", + host: func(in *Host) *Host { + return nil + }, + sets: func() []string { + return nil + }, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{ + dirtyHost: true, + ipsToAdd: baseIpsIfaces, + dnsNamesToAdd: baseDnsNamesIfaces, + } + return in, hi + }, + }, + { + name: "no-change", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{} + return in, hi + }, + }, + { + name: "new-name", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + in.Name = "newname" + hi := &hostInfo{ + dirtyHost: true, + } + return in, hi + }, + }, + { + name: "new-description", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + in.Description = "newdescription" + hi := &hostInfo{ + dirtyHost: true, + } + return in, hi + }, + }, + { + name: "extra-ip", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + const newIp = "11.22.33.44" + in.IpAddresses = append(in.IpAddresses, newIp) + ip, err := host.NewIpAddress(ctx, baseHostId, newIp) + require.NoError(t, err) + hi := &hostInfo{ + ipsToAdd: valueToInterfaceMap{newIp: ip}, + } + return in, hi + }, + }, + { + name: "remove-ip", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{ + ipsToRemove: valueToInterfaceMap{}, + } + for _, ip := range in.IpAddresses[1:] { + hi.ipsToRemove[ip] = baseIpsIfaces[ip] + } + in.IpAddresses = in.IpAddresses[0:1] + return in, hi + }, + }, + { + name: "extra-dns-name", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + const newName = "this.is.a.test" + in.DnsNames = append(in.DnsNames, newName) + name, err := host.NewDnsName(ctx, baseHostId, newName) + require.NoError(t, err) + hi := &hostInfo{ + dnsNamesToAdd: valueToInterfaceMap{newName: name}, + } + return in, hi + }, + }, + { + name: "remove-dns-name", + host: defaultHostFunc, + sets: defaultSetsFunc, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{ + dnsNamesToRemove: valueToInterfaceMap{}, + } + for _, name := range in.DnsNames[1:] { + hi.dnsNamesToRemove[name] = baseDnsNamesIfaces[name] + } + in.DnsNames = in.DnsNames[0:1] + return in, hi + }, + }, + { + name: "add-sets", + host: defaultHostFunc, + sets: func() []string { + return append(defaultSetsFunc(), "extra-set", "extra-set-2") + }, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{} + return in, hi + }, + setsToAdd: map[string][]string{ + "extra-set": {baseHostId}, + "extra-set-2": {baseHostId}, + }, + }, + { + name: "remove-sets", + host: defaultHostFunc, + sets: func() []string { + return defaultSetsFunc()[0:1] + }, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{} + return in, hi + }, + setsToRemove: map[string][]string{ + "set2": {baseHostId}, + }, + }, + { + name: "add-and-remove-sets", + host: defaultHostFunc, + sets: func() []string { + return append(defaultSetsFunc()[0:1], "extra-set") + }, + in: func(in *plgpb.ListHostsResponseHost) (*plgpb.ListHostsResponseHost, *hostInfo) { + hi := &hostInfo{} + return in, hi + }, + setsToAdd: map[string][]string{ + "extra-set": {baseHostId}, + }, + setsToRemove: map[string][]string{ + "set2": {baseHostId}, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require, assert := require.New(t), assert.New(t) + var hi *hostInfo + currHost := tt.host(baseHost.clone()) + currentHostMap := map[string]*Host{} + if currHost != nil { + currentHostMap[baseHostId] = currHost + } + h := proto.Clone(baseResponseHost).(*plgpb.ListHostsResponseHost) + h, hi = tt.in(h) + + newHostMap, err := createNewHostMap(ctx, catalog, []*plgpb.ListHostsResponseHost{h}, currentHostMap) + require.NoError(err) + require.NotNil(newHostMap) + require.NotNil(newHostMap[baseHostId]) + got := newHostMap[baseHostId] + + // Check the various host/hostinfo bits + { + assert.Equal(h.ExternalId, got.h.ExternalId) + assert.Equal(h.Name, got.h.Name) + assert.Equal(h.Description, got.h.Description) + assert.ElementsMatch(h.IpAddresses, got.h.IpAddresses) + assert.ElementsMatch(h.DnsNames, got.h.DnsNames) + assert.ElementsMatch(h.SetIds, got.h.SetIds) + + assert.Equal(hi.dirtyHost, got.dirtyHost) + assert.Empty( + cmp.Diff( + hi.ipsToAdd, + got.ipsToAdd, + cmpopts.IgnoreUnexported(host.IpAddress{}, hoststore.IpAddress{}), + cmpopts.SortSlices(func(x, y interface{}) bool { + return x.(*host.IpAddress).Address < y.(*host.IpAddress).Address + }), + ), + ) + assert.Empty( + cmp.Diff( + hi.ipsToRemove, + got.ipsToRemove, + cmpopts.IgnoreUnexported(host.IpAddress{}, hoststore.IpAddress{}), + cmpopts.SortSlices(func(x, y interface{}) bool { + return x.(*host.IpAddress).Address < y.(*host.IpAddress).Address + }), + ), + ) + assert.Empty( + cmp.Diff( + hi.dnsNamesToAdd, + got.dnsNamesToAdd, + cmpopts.IgnoreUnexported(host.DnsName{}, hoststore.DnsName{}), + cmpopts.SortSlices(func(x, y interface{}) bool { + return x.(*host.DnsName).Name < y.(*host.DnsName).Name + }), + ), + ) + assert.Empty( + cmp.Diff( + hi.dnsNamesToRemove, + got.dnsNamesToRemove, + cmpopts.IgnoreUnexported(host.DnsName{}, hoststore.DnsName{}), + cmpopts.SortSlices(func(x, y interface{}) bool { + return x.(*host.DnsName).Name < y.(*host.DnsName).Name + }), + ), + ) + } + + // Run through the sets function + { + newHostMap[baseHostId].h.SetIds = tt.sets() + toAdd, toRemove, _ := getSetChanges(currentHostMap, newHostMap) + assert.Equal(tt.setsToAdd, toAdd) + assert.Equal(tt.setsToRemove, toRemove) + } + }) + } +} diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index 45d38c42e..c3c7e20c5 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -18,7 +18,19 @@ import ( "github.com/stretchr/testify/require" ) -// TestCatalog creates count number of plugin host catalogs to the provided DB +// TestCatalogs creates count number of static host catalogs to the provided DB +// with the provided scope id. If any errors are encountered during the creation of +// the host catalog, the test will fail. +func TestCatalogs(t *testing.T, conn *db.DB, scopeId, pluginId string, count int) []*HostCatalog { + t.Helper() + var cats []*HostCatalog + for i := 0; i < count; i++ { + cats = append(cats, TestCatalog(t, conn, scopeId, pluginId)) + } + return cats +} + +// TestCatalog creates a plugin host catalogs to the provided DB // with the provided scope id. If any errors are encountered during the creation of // the host catalog, the test will fail. func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Option) *HostCatalog { @@ -78,6 +90,10 @@ func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, coun require := require.New(t) retRH := make([]*plgpb.ListHostsResponseHost, 0, count) retH := make([]*Host, 0, count) + if setIds == nil { + // Prevent panics + setIds = make([]string, 0) + } for i := 0; i < count; i++ { externalId, err := base62.Random(10) @@ -86,19 +102,25 @@ func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, coun ipStr := testGetIpAddress(t) dnsName := testGetDnsName(t) - retRH = append(retRH, &plgpb.ListHostsResponseHost{ + rh := &plgpb.ListHostsResponseHost{ ExternalId: externalId, - SetIds: setIds, + Name: base62.MustRandom(10), + Description: base62.MustRandom(10), + SetIds: setIds[0 : i+1], IpAddresses: []string{ipStr}, DnsNames: []string{dnsName}, - }) + } + retRH = append(retRH, rh) publicId, err := newHostId(context.Background(), catalog.PublicId, externalId) require.NoError(err) retH = append(retH, &Host{ PluginId: catalog.PluginId, + SetIds: setIds[0 : i+1], Host: &store.Host{ + Name: rh.Name, + Description: rh.Description, CatalogId: catalog.PublicId, PublicId: publicId, ExternalId: externalId, diff --git a/internal/proto/local/plugin/v1/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto index 4de9aaf58..9b7b51c89 100644 --- a/internal/proto/local/plugin/v1/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -149,15 +149,21 @@ message ListHostsResponseHost { // useful, ie: a compute instance ID. string external_id = 10; + // If supplied, will be set as the managed name of the host + string name = 20; + + // If supplied, will be set as the managed description of the host + string description = 30; + // Required. Any and all known IP addresses for the host. - repeated string ip_addresses = 20; + repeated string ip_addresses = 40; // Required. Any and all known DNS names for the host. - repeated string dns_names = 30; + repeated string dns_names = 50; // Required. The host set IDs that match this host, out of the host sets // sent in the request. - repeated string set_ids = 40; + repeated string set_ids = 60; // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index 459450ee9..1ea2a7695 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -785,13 +785,17 @@ type ListHostsResponseHost struct { // included in audit logs. It should be set to something unique and // useful, ie: a compute instance ID. ExternalId string `protobuf:"bytes,10,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + // If supplied, will be set as the managed name of the host + Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` + // If supplied, will be set as the managed description of the host + Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty"` // Required. Any and all known IP addresses for the host. - IpAddresses []string `protobuf:"bytes,20,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` + IpAddresses []string `protobuf:"bytes,40,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` // Required. Any and all known DNS names for the host. - DnsNames []string `protobuf:"bytes,30,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` + DnsNames []string `protobuf:"bytes,50,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` // Required. The host set IDs that match this host, out of the host sets // sent in the request. - SetIds []string `protobuf:"bytes,40,rep,name=set_ids,json=setIds,proto3" json:"set_ids,omitempty"` + SetIds []string `protobuf:"bytes,60,rep,name=set_ids,json=setIds,proto3" json:"set_ids,omitempty"` // Optional. Provider-specific metadata that is applicable to this // host. Example: host descriptions, tags, alternate network // addresses, etc. @@ -837,6 +841,20 @@ func (x *ListHostsResponseHost) GetExternalId() string { return "" } +func (x *ListHostsResponseHost) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ListHostsResponseHost) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + func (x *ListHostsResponseHost) GetIpAddresses() []string { if x != nil { return x.IpAddresses @@ -1058,66 +1076,70 @@ var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0xca, - 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x80, + 0x02, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x1e, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x65, 0x74, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x74, 0x49, - 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x18, 0x64, 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, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x14, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x64, - 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, 0x07, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x32, 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, - 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, - 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, + 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x3c, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x65, 0x74, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x22, 0x49, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x73, 0x18, 0x64, 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, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x32, 0xd3, 0x04, 0x0a, + 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, + 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, + 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, + 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, - 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, - 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, + 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, + 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, + 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, + 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( From 6eba8a4787033770aee36a42f73b6d4321211d6c Mon Sep 17 00:00:00 2001 From: Todd Date: Thu, 28 Oct 2021 12:46:42 -0700 Subject: [PATCH 41/71] Lookups Catalogs and Persisted data together (#1643) * Fixed a number of TODOs: 1. Create a catalog/persisted data view. 2. Pass all sets to the plugin when deleting a catalog. --- .../oss/postgres/19/05_plugin_host.up.sql | 22 ++ internal/host/plugin/host_catalog.go | 53 +++ internal/host/plugin/repository.go | 3 - .../host/plugin/repository_host_catalog.go | 118 +++--- .../plugin/repository_host_catalog_test.go | 10 +- internal/host/plugin/repository_host_set.go | 32 +- .../host/plugin/repository_host_set_test.go | 10 +- .../local/plugin/v1/host_plugin_service.proto | 5 +- plugins/host/assets.go | 2 +- sdk/pbs/plugin/host_plugin_service.pb.go | 348 +++++++++--------- sdk/plugins/host/load.go | 2 +- 11 files changed, 344 insertions(+), 261 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql index fa17b888b..048ab99e0 100644 --- a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql @@ -335,6 +335,28 @@ begin; ('host_plugin_set', 1), ('host_plugin_host', 1); + -- host_plugin_catalog_with_secret is useful for reading a plugin catalog with + -- its associated persisted data. + create view host_plugin_catalog_with_secret as + select + hc.public_id, + hc.scope_id, + hc.plugin_id, + hc.name, + hc.description, + hc.create_time, + hc.update_time, + hc.version, + hc.attributes, + hcs.secret, + hcs.key_id, + hcs.create_time as persisted_create_time, + hcs.update_time as persisted_update_time + from + host_plugin_catalog hc + left outer join host_plugin_catalog_secret hcs on hc.public_id = hcs.catalog_id; + comment on view host_plugin_catalog_with_secret is + 'host plugin catalog with its associated persisted data'; -- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a -- plugin host with its associated value objects (ip addresses, dns names) and diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index df151fef0..05320c550 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -6,6 +6,7 @@ package plugin import ( "context" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/oplog" @@ -96,3 +97,55 @@ func (s *HostCatalog) oplog(op oplog.OpType) oplog.Metadata { } return metadata } + +type catalogAgg struct { + PublicId string `gorm:"primary_key"` + ScopeId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + Version uint32 + Attributes []byte + Secret []byte + KeyId string + PersistedCreateTime *timestamp.Timestamp + PersistedUpdateTime *timestamp.Timestamp +} + +func (agg *catalogAgg) toCatalogAndPersisted() (*HostCatalog, *HostCatalogSecret) { + if agg == nil { + return nil, nil + } + c := allocHostCatalog() + c.PublicId = agg.PublicId + c.ScopeId = agg.ScopeId + c.PluginId = agg.PluginId + c.Name = agg.Name + c.Description = agg.Description + c.CreateTime = agg.CreateTime + c.UpdateTime = agg.UpdateTime + c.Version = agg.Version + c.Attributes = agg.Attributes + + var s *HostCatalogSecret + if len(agg.Secret) > 0 { + s = allocHostCatalogSecret() + s.CatalogId = agg.PublicId + s.Secret = agg.Secret + s.KeyId = agg.KeyId + s.CreateTime = agg.PersistedCreateTime + s.UpdateTime = agg.PersistedUpdateTime + } + return c, s +} + +// TableName returns the table name for gorm +func (agg *catalogAgg) TableName() string { + return "host_plugin_catalog_with_secret" +} + +func (agg *catalogAgg) GetPublicId() string { + return agg.PublicId +} diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index e40a756bd..9ebf661b1 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -19,9 +19,6 @@ type Repository struct { kms *kms.Kms // plugins is a map from plugin resource id to host plugin client. - // - // TODO: When we are using go-plugin change from using a - // plgpb.HostPluginServiceServer to the client. plugins map[string]plgpb.HostPluginServiceClient // defaultLimit provides a default for limiting the number of results // returned from the repo diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index a7353eaf9..aba27864a 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -8,9 +8,11 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + pbset "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -55,14 +57,14 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } c.PublicId = id - plgClient, ok := r.plugins[c.GetPluginId()] - if !ok || plgClient == nil { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) - } plgHc, err := toPluginCatalog(ctx, c) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) } + plgClient, ok := r.plugins[c.GetPluginId()] + if !ok || plgClient == nil { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) + } plgResp, err := plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc}) if err != nil { if status.Code(err) != codes.Unimplemented { @@ -150,11 +152,10 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) if id == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - c, err := r.getCatalog(ctx, id) + c, _, err := r.getCatalog(ctx, id) if errors.IsNotFoundError(err) { return nil, nil, nil } - if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) } @@ -203,33 +204,40 @@ func (r *Repository) DeleteCatalog(ctx context.Context, id string, _ ...Option) return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - c, err := r.getCatalog(ctx, id) + c, p, err := r.getCatalog(ctx, id) if err != nil && !errors.IsNotFoundError(err) { return db.NoRowsAffected, errors.Wrap(ctx, err, op) } if c == nil { return db.NoRowsAffected, nil } - p, err := r.getPersistedDataForCatalog(ctx, c) + plgHc, err := toPluginCatalog(ctx, c) if err != nil { return db.NoRowsAffected, errors.Wrap(ctx, err, op) } + sets, _, err := r.getSets(ctx, "", c.GetPublicId()) + var plgSets []*pbset.HostSet + for _, s := range sets { + ps, err := toPluginSet(ctx, s) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + plgSets = append(plgSets, ps) + } plgClient, ok := r.plugins[c.GetPluginId()] if !ok || plgClient == nil { return 0, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) } - plgHc, err := toPluginCatalog(ctx, c) - if err != nil { - return 0, errors.Wrap(ctx, err, op) - } - // TODO: Include all the sets for this catalog in the delete catalog request. We don't need it for now - // since our currently provided plugins only read data for set configuration, but in the future that might - // change. - _, err = plgClient.OnDeleteCatalog(ctx, &plgpb.OnDeleteCatalogRequest{Catalog: plgHc, Persisted: p}) + _, err = plgClient.OnDeleteCatalog(ctx, &plgpb.OnDeleteCatalogRequest{ + Catalog: plgHc, + Sets: plgSets, + Persisted: p, + }) if err != nil { // Even if the plugin returns an error, we ignore it and proceed with // deleting the catalog. + event.WriteError(ctx, op, err, event.WithInfoMsg("plugin deleting catalog", "host plugin id", c.GetPluginId())) } oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) @@ -271,50 +279,23 @@ func (r *Repository) DeleteCatalog(ctx context.Context, id string, _ ...Option) // getCatalog retrieves the *HostCatalog with the provided id. If it is not found or there // is an problem getting it from the database an error is returned instead. -func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, error) { +func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, *plgpb.HostCatalogPersisted, error) { const op = "plugin.(Repository).getCatalog" - c := allocHostCatalog() - c.PublicId = id - if err := r.reader.LookupByPublicId(ctx, c); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) + ca := &catalogAgg{} + ca.PublicId = id + if err := r.reader.LookupByPublicId(ctx, ca); err != nil { + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id))) } - return c, nil -} - -// getPersistedDataForCatalog returns the persisted data for a catalog if -// present. c must have a valid Public Id and Scope Id set. -// TODO: consider merging the functions for getting catalog and persisted data into a view. -func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCatalog) (*plgpb.HostCatalogPersisted, error) { - const op = "plugin.(Repository).getPersistedDataForCatalog" - if c.PublicId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "empty public id") - } - if c.ScopeId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "empty scope id") - } - cSecret := allocHostCatalogSecret() - if err := r.reader.LookupWhere(ctx, cSecret, "catalog_id=?", c.GetPublicId()); err != nil { - if errors.IsNotFoundError(err) { - return nil, nil + c, s := ca.toCatalogAndPersisted() + var p *plgpb.HostCatalogPersisted + if s != nil { + var err error + p, err = r.toPluginPersistedData(ctx, c.GetScopeId(), s) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) } - return nil, errors.Wrap(ctx, err, op) } - if cSecret == nil { - return nil, nil - } - dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) - } - if err := cSecret.decrypt(ctx, dbWrapper); err != nil { - return nil, errors.Wrap(ctx, err, op) - } - - secrets := &structpb.Struct{} - if err := proto.Unmarshal(cSecret.GetSecret(), secrets); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unmarshaling secret")) - } - return &plgpb.HostCatalogPersisted{Secrets: secrets}, nil + return c, p, nil } func (r *Repository) getPlugin(ctx context.Context, plgId string) (*hostplugin.Plugin, error) { @@ -353,3 +334,28 @@ func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, err } return hc, nil } + +// toPluginCatalog converts a *HostCatalogSecret from storage to a +// *plgpb.HostCatalogPersisted expected by a plugin. Scope Id must be set. +func (r *Repository) toPluginPersistedData(ctx context.Context, scopeId string, cSecret *HostCatalogSecret) (*plgpb.HostCatalogPersisted, error) { + const op = "plugin.(Repository).getPersistedDataForCatalog" + if scopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty scope id") + } + if cSecret == nil { + return nil, nil + } + dbWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } + if err := cSecret.decrypt(ctx, dbWrapper); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + secrets := &structpb.Struct{} + if err := proto.Unmarshal(cSecret.GetSecret(), secrets); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unmarshaling secret")) + } + return &plgpb.HostCatalogPersisted{Secrets: secrets}, nil +} diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 15f606bdc..5ea129d90 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -495,11 +495,11 @@ func TestRepository_DeleteCatalog(t *testing.T) { assert.NotNil(t, repo) tests := []struct { - name string + name string id string pluginChecker func(*testing.T, *plgpb.OnDeleteCatalogRequest) error want int - wantErr errors.Code + wantErr errors.Code }{ { name: "found", @@ -529,8 +529,8 @@ func TestRepository_DeleteCatalog(t *testing.T) { want: 0, }, { - name: "bad-public-id", - id: "", + name: "bad-public-id", + id: "", pluginChecker: func(t *testing.T, req *plgpb.OnDeleteCatalogRequest) error { assert.Fail(t, "Should not call the plugin for a bad id") return nil @@ -621,4 +621,4 @@ func TestRepository_DeleteCatalogX(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 96b1acf40..cf755b8b1 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -54,14 +54,10 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } s = s.clone() - c, err := r.getCatalog(ctx, s.CatalogId) + c, per, err := r.getCatalog(ctx, s.CatalogId) if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog")) } - per, err := r.getPersistedDataForCatalog(ctx, c) - if err != nil { - return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up persisted data")) - } id, err := newHostSetId(ctx) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) @@ -187,7 +183,7 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host // FIXME: change to use the database if plg != nil && opts.WithSetMembers { - cat, err := r.getCatalog(ctx, setToReturn.GetCatalogId()) + cat, persisted, err := r.getCatalog(ctx, setToReturn.GetCatalogId()) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op) } @@ -195,10 +191,6 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op) } - persisted, err := r.getPersistedDataForCatalog(ctx, cat) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } plgSet, err := toPluginSet(ctx, setToReturn) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op) @@ -270,17 +262,13 @@ func (r *Repository) DeleteSet(ctx context.Context, scopeId string, publicId str } s := sets[0] - c, err := r.getCatalog(ctx, s.GetCatalogId()) + c, p, err := r.getCatalog(ctx, s.GetCatalogId()) if err != nil && errors.IsNotFoundError(err) { return db.NoRowsAffected, errors.Wrap(ctx, err, op) } if c == nil { return db.NoRowsAffected, nil } - p, err := r.getPersistedDataForCatalog(ctx, c) - if err != nil { - return db.NoRowsAffected, errors.Wrap(ctx, err, op) - } plgClient, ok := r.plugins[plg.GetPublicId()] if !ok || plgClient == nil { @@ -561,14 +549,15 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En for k := range catalogInfos { catIds = append(catIds, k) } - var cats []*HostCatalog - if err := r.reader.SearchWhere(ctx, &cats, "public_id in (?)", []interface{}{catIds}); err != nil { + var catAggs []*catalogAgg + if err := r.reader.SearchWhere(ctx, &catAggs, "public_id in (?)", []interface{}{catIds}); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve catalogs %v", catIds))) } - if len(cats) == 0 { + if len(catAggs) == 0 { return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "no catalogs returned for retrieved sets") } - for _, c := range cats { + for _, ca := range catAggs { + c, s := ca.toCatalogAndPersisted() ci, ok := catalogInfos[c.GetPublicId()] if !ok { return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "catalog returned when no set requested it") @@ -579,10 +568,9 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En } ci.plgCat = plgCat - // TODO: Do these lookups from the DB in bulk instead of individually. - per, err := r.getPersistedDataForCatalog(ctx, c) + per, err := r.toPluginPersistedData(ctx, c.GetScopeId(), s) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("persisted catalog lookup failed")) + return nil, errors.Wrap(ctx, err, op) } ci.persisted = per catalogInfos[c.GetPublicId()] = ci diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index c915f5d93..0a8895772 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -677,14 +677,14 @@ func TestRepository_DeleteSet(t *testing.T) { newHostSetId, err := newHostSetId(ctx) require.NoError(t, err) tests := []struct { - name string - in string + name string + in string pluginChecker func(*plgpb.OnDeleteSetRequest) error - want int - wantIsErr errors.Code + want int + wantIsErr errors.Code }{ { - name: "With no public id", + name: "With no public id", pluginChecker: func(req *plgpb.OnDeleteSetRequest) error { assert.Fail(t, "The plugin shouldn't be called") return nil diff --git a/internal/proto/local/plugin/v1/host_plugin_service.proto b/internal/proto/local/plugin/v1/host_plugin_service.proto index 9b7b51c89..e016c4406 100644 --- a/internal/proto/local/plugin/v1/host_plugin_service.proto +++ b/internal/proto/local/plugin/v1/host_plugin_service.proto @@ -76,9 +76,12 @@ message OnUpdateCatalogResponse { message OnDeleteCatalogRequest { // The existing state of the catalog to delete. controller.api.resources.hostcatalogs.v1.HostCatalog catalog = 10; + + // The host sets contained in the catalog being deleted. + repeated controller.api.resources.hostsets.v1.HostSet sets = 20; // The existing persisted secret data. - HostCatalogPersisted persisted = 20; + HostCatalogPersisted persisted = 30; } message OnDeleteCatalogResponse {} diff --git a/plugins/host/assets.go b/plugins/host/assets.go index 4790c5733..83447e592 100644 --- a/plugins/host/assets.go +++ b/plugins/host/assets.go @@ -1,4 +1,4 @@ -package host_plugin_assets +package host_plugin_assets import ( "embed" diff --git a/sdk/pbs/plugin/host_plugin_service.pb.go b/sdk/pbs/plugin/host_plugin_service.pb.go index 1ea2a7695..951f8d127 100644 --- a/sdk/pbs/plugin/host_plugin_service.pb.go +++ b/sdk/pbs/plugin/host_plugin_service.pb.go @@ -251,8 +251,10 @@ type OnDeleteCatalogRequest struct { // The existing state of the catalog to delete. Catalog *hostcatalogs.HostCatalog `protobuf:"bytes,10,opt,name=catalog,proto3" json:"catalog,omitempty"` + // The host sets contained in the catalog being deleted. + Sets []*hostsets.HostSet `protobuf:"bytes,20,rep,name=sets,proto3" json:"sets,omitempty"` // The existing persisted secret data. - Persisted *HostCatalogPersisted `protobuf:"bytes,20,opt,name=persisted,proto3" json:"persisted,omitempty"` + Persisted *HostCatalogPersisted `protobuf:"bytes,30,opt,name=persisted,proto3" json:"persisted,omitempty"` } func (x *OnDeleteCatalogRequest) Reset() { @@ -294,6 +296,13 @@ func (x *OnDeleteCatalogRequest) GetCatalog() *hostcatalogs.HostCatalog { return nil } +func (x *OnDeleteCatalogRequest) GetSets() []*hostsets.HostSet { + if x != nil { + return x.Sets + } + return nil +} + func (x *OnDeleteCatalogRequest) GetPersisted() *HostCatalogPersisted { if x != nil { return x.Persisted @@ -992,154 +1001,158 @@ var file_plugin_v1_host_plugin_service_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, - 0xa8, 0x01, 0x0a, 0x16, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, + 0xeb, 0x01, 0x0a, 0x16, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x09, 0x70, - 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, - 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x4f, 0x6e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3f, 0x0a, - 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3d, + 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x41, 0x0a, 0x04, 0x73, + 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x04, 0x73, 0x65, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, - 0x13, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x02, 0x0a, 0x12, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x4e, 0x0a, 0x0b, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, - 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, - 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x46, 0x0a, 0x07, - 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x19, 0x0a, + 0x17, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x4f, 0x6e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, + 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x12, 0x3f, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x06, 0x6e, 0x65, - 0x77, 0x53, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, - 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x4f, - 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, - 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x12, 0x3f, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, - 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, - 0x73, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, - 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, - 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x4c, 0x69, - 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x03, 0x73, 0x65, + 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x1e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, + 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x02, 0x0a, 0x12, 0x4f, 0x6e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, - 0x41, 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, 0x04, 0x73, 0x65, - 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, - 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x64, 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, - 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x80, - 0x02, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, - 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, - 0x17, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x3c, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x65, 0x74, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x22, 0x49, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x18, 0x64, 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, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x32, 0xd3, 0x04, 0x0a, - 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x4e, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x14, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, + 0x46, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, + 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, + 0x06, 0x6e, 0x65, 0x77, 0x53, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, + 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, + 0x0a, 0x12, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x3f, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, + 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, + 0x74, 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, + 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, + 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, + 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x65, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, + 0x73, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x52, + 0x04, 0x73, 0x65, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x52, 0x09, 0x70, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x64, 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x68, 0x6f, 0x73, + 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, + 0x73, 0x22, 0x80, 0x02, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x18, 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x3c, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x74, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 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, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x07, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x64, 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, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x32, + 0xd3, 0x04, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, - 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, - 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, - 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, - 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, - 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, - 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, + 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4c, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1d, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, + 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1184,42 +1197,43 @@ var file_plugin_v1_host_plugin_service_proto_depIdxs = []int32{ 15, // 4: plugin.v1.OnUpdateCatalogRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted 15, // 5: plugin.v1.OnUpdateCatalogResponse.persisted:type_name -> plugin.v1.HostCatalogPersisted 16, // 6: plugin.v1.OnDeleteCatalogRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 15, // 7: plugin.v1.OnDeleteCatalogRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted - 16, // 8: plugin.v1.OnCreateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 9: plugin.v1.OnCreateSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 10: plugin.v1.OnCreateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted - 16, // 11: plugin.v1.OnUpdateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 12: plugin.v1.OnUpdateSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet - 17, // 13: plugin.v1.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 14: plugin.v1.OnUpdateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted - 16, // 15: plugin.v1.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 16: plugin.v1.OnDeleteSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 17: plugin.v1.OnDeleteSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted - 16, // 18: plugin.v1.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog - 17, // 19: plugin.v1.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet - 15, // 20: plugin.v1.ListHostsRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted - 14, // 21: plugin.v1.ListHostsResponse.hosts:type_name -> plugin.v1.ListHostsResponseHost - 18, // 22: plugin.v1.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct - 18, // 23: plugin.v1.HostCatalogPersisted.secrets:type_name -> google.protobuf.Struct - 0, // 24: plugin.v1.HostPluginService.OnCreateCatalog:input_type -> plugin.v1.OnCreateCatalogRequest - 2, // 25: plugin.v1.HostPluginService.OnUpdateCatalog:input_type -> plugin.v1.OnUpdateCatalogRequest - 4, // 26: plugin.v1.HostPluginService.OnDeleteCatalog:input_type -> plugin.v1.OnDeleteCatalogRequest - 6, // 27: plugin.v1.HostPluginService.OnCreateSet:input_type -> plugin.v1.OnCreateSetRequest - 8, // 28: plugin.v1.HostPluginService.OnUpdateSet:input_type -> plugin.v1.OnUpdateSetRequest - 10, // 29: plugin.v1.HostPluginService.OnDeleteSet:input_type -> plugin.v1.OnDeleteSetRequest - 12, // 30: plugin.v1.HostPluginService.ListHosts:input_type -> plugin.v1.ListHostsRequest - 1, // 31: plugin.v1.HostPluginService.OnCreateCatalog:output_type -> plugin.v1.OnCreateCatalogResponse - 3, // 32: plugin.v1.HostPluginService.OnUpdateCatalog:output_type -> plugin.v1.OnUpdateCatalogResponse - 5, // 33: plugin.v1.HostPluginService.OnDeleteCatalog:output_type -> plugin.v1.OnDeleteCatalogResponse - 7, // 34: plugin.v1.HostPluginService.OnCreateSet:output_type -> plugin.v1.OnCreateSetResponse - 9, // 35: plugin.v1.HostPluginService.OnUpdateSet:output_type -> plugin.v1.OnUpdateSetResponse - 11, // 36: plugin.v1.HostPluginService.OnDeleteSet:output_type -> plugin.v1.OnDeleteSetResponse - 13, // 37: plugin.v1.HostPluginService.ListHosts:output_type -> plugin.v1.ListHostsResponse - 31, // [31:38] is the sub-list for method output_type - 24, // [24:31] is the sub-list for method input_type - 24, // [24:24] is the sub-list for extension type_name - 24, // [24:24] is the sub-list for extension extendee - 0, // [0:24] is the sub-list for field type_name + 17, // 7: plugin.v1.OnDeleteCatalogRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 8: plugin.v1.OnDeleteCatalogRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 9: plugin.v1.OnCreateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 10: plugin.v1.OnCreateSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 11: plugin.v1.OnCreateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 12: plugin.v1.OnUpdateSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 13: plugin.v1.OnUpdateSetRequest.current_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 17, // 14: plugin.v1.OnUpdateSetRequest.new_set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 15: plugin.v1.OnUpdateSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 16: plugin.v1.OnDeleteSetRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 17: plugin.v1.OnDeleteSetRequest.set:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 18: plugin.v1.OnDeleteSetRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 16, // 19: plugin.v1.ListHostsRequest.catalog:type_name -> controller.api.resources.hostcatalogs.v1.HostCatalog + 17, // 20: plugin.v1.ListHostsRequest.sets:type_name -> controller.api.resources.hostsets.v1.HostSet + 15, // 21: plugin.v1.ListHostsRequest.persisted:type_name -> plugin.v1.HostCatalogPersisted + 14, // 22: plugin.v1.ListHostsResponse.hosts:type_name -> plugin.v1.ListHostsResponseHost + 18, // 23: plugin.v1.ListHostsResponseHost.attributes:type_name -> google.protobuf.Struct + 18, // 24: plugin.v1.HostCatalogPersisted.secrets:type_name -> google.protobuf.Struct + 0, // 25: plugin.v1.HostPluginService.OnCreateCatalog:input_type -> plugin.v1.OnCreateCatalogRequest + 2, // 26: plugin.v1.HostPluginService.OnUpdateCatalog:input_type -> plugin.v1.OnUpdateCatalogRequest + 4, // 27: plugin.v1.HostPluginService.OnDeleteCatalog:input_type -> plugin.v1.OnDeleteCatalogRequest + 6, // 28: plugin.v1.HostPluginService.OnCreateSet:input_type -> plugin.v1.OnCreateSetRequest + 8, // 29: plugin.v1.HostPluginService.OnUpdateSet:input_type -> plugin.v1.OnUpdateSetRequest + 10, // 30: plugin.v1.HostPluginService.OnDeleteSet:input_type -> plugin.v1.OnDeleteSetRequest + 12, // 31: plugin.v1.HostPluginService.ListHosts:input_type -> plugin.v1.ListHostsRequest + 1, // 32: plugin.v1.HostPluginService.OnCreateCatalog:output_type -> plugin.v1.OnCreateCatalogResponse + 3, // 33: plugin.v1.HostPluginService.OnUpdateCatalog:output_type -> plugin.v1.OnUpdateCatalogResponse + 5, // 34: plugin.v1.HostPluginService.OnDeleteCatalog:output_type -> plugin.v1.OnDeleteCatalogResponse + 7, // 35: plugin.v1.HostPluginService.OnCreateSet:output_type -> plugin.v1.OnCreateSetResponse + 9, // 36: plugin.v1.HostPluginService.OnUpdateSet:output_type -> plugin.v1.OnUpdateSetResponse + 11, // 37: plugin.v1.HostPluginService.OnDeleteSet:output_type -> plugin.v1.OnDeleteSetResponse + 13, // 38: plugin.v1.HostPluginService.ListHosts:output_type -> plugin.v1.ListHostsResponse + 32, // [32:39] is the sub-list for method output_type + 25, // [25:32] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_plugin_v1_host_plugin_service_proto_init() } diff --git a/sdk/plugins/host/load.go b/sdk/plugins/host/load.go index ac55017ff..fa50c2bb9 100644 --- a/sdk/plugins/host/load.go +++ b/sdk/plugins/host/load.go @@ -179,7 +179,7 @@ func executeHostPlugin(plugin pluginInfo, opt ...Option) (pb.HostPluginServiceCl dir = tmpDir } pluginPath := filepath.Join(dir, executedFileName) - if err := ioutil.WriteFile(pluginPath, buf, fs.FileMode(0700)); err != nil { + if err := ioutil.WriteFile(pluginPath, buf, fs.FileMode(0o700)); err != nil { return nil, cleanup, fmt.Errorf("error writing out host plugin for execution: %w", err) } From 5e782b6eccf6135c48f6e6079fd02ccccfd412b2 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 29 Oct 2021 15:35:06 -0400 Subject: [PATCH 42/71] Update view to return host IDs in a set on set lookup (#1646) This removes the optional call to the plugin. --- .../postgres/19/06_preferred_endpoints.up.sql | 36 +++-- internal/host/options.go | 11 -- internal/host/plugin/host.go | 3 +- internal/host/plugin/host_set.go | 90 ++++++++++- internal/host/plugin/host_set_member_test.go | 103 ++++++++---- internal/host/plugin/options.go | 8 + internal/host/plugin/options_test.go | 6 + internal/host/plugin/repository_host_set.go | 147 +----------------- .../host/plugin/repository_host_set_test.go | 2 +- .../handlers/host_sets/host_set_service.go | 6 +- 10 files changed, 206 insertions(+), 206 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql index aedabd48e..cba52df65 100644 --- a/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql @@ -50,23 +50,25 @@ create trigger immutable_preferred_endpoint -- values. The delimiter depends on the value objects (e.g. if they need -- ordering). create view host_plugin_host_set_with_value_obj as -select - hs.public_id, - hs.catalog_id, - hc.plugin_id, - hs.name, - hs.description, - hs.create_time, - hs.update_time, - hs.version, - hs.attributes, - -- the string_agg(..) column will be null if there are no associated value objects - string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints -from - host_plugin_set hs - join host_plugin_catalog hc on hs.catalog_id = hc.public_id - left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id -group by hs.public_id, hc.plugin_id; + select + hs.public_id, + hs.catalog_id, + hc.plugin_id, + hs.name, + hs.description, + hs.create_time, + hs.update_time, + hs.version, + hs.attributes, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints, + string_agg(distinct hpsm.host_id, '|') as host_ids + from + host_plugin_set hs + join host_plugin_catalog hc on hs.catalog_id = hc.public_id + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id + left outer join host_plugin_set_member hpsm on hs.public_id = hpsm.set_id + group by hs.public_id, hc.plugin_id; comment on view host_plugin_host_set_with_value_obj is 'host plugin host set with its associated value objects'; diff --git a/internal/host/options.go b/internal/host/options.go index 87d27c155..6e2afe4d6 100644 --- a/internal/host/options.go +++ b/internal/host/options.go @@ -19,7 +19,6 @@ type options struct { WithLimit int WithOrderByCreateTime bool Ascending bool - WithSetMembers bool } func getDefaultOptions() options { @@ -45,13 +44,3 @@ func WithOrderByCreateTime(ascending bool) Option { return nil } } - -// WithSetMembers controls whether to include set members in a lookup. This will -// always be true for static and may be true for plugin when we have caching, -// but for now this skips API calls on authentication. -func WithSetMembers(with bool) Option { - return func(o *options) error { - o.WithSetMembers = true - return nil - } -} diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go index 2da402973..73db33d15 100644 --- a/internal/host/plugin/host.go +++ b/internal/host/plugin/host.go @@ -24,13 +24,14 @@ type Host struct { // newHost creates a new in memory Host assigned to catalogId with an address. // Supported options: WithName, WithDescription, WithIpAddresses, WithDnsNames, -// WithPluginId. Others ignored. +// WithPluginId, WithPublicId. Others ignored. func NewHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host { opts := getOpts(opt...) h := &Host{ PluginId: opts.withPluginId, Host: &store.Host{ + PublicId: opts.withPublicId, CatalogId: catalogId, ExternalId: externalId, Name: opts.withName, diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index e0703c83a..ee18fd8d9 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -2,7 +2,12 @@ package plugin import ( "context" + "fmt" + "sort" + "strconv" + "strings" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/oplog" @@ -12,7 +17,9 @@ import ( // A HostSet is a collection of hosts from the set's catalog. type HostSet struct { *store.HostSet - tableName string `gorm:"-"` + PluginId string `gorm:"-"` + HostIds []string `gorm:"-"` + tableName string `gorm:"-"` } // NewHostSet creates a new in memory HostSet assigned to catalogId. Attributes, @@ -81,3 +88,84 @@ func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata { } return metadata } + +// hostSetAgg is a view that aggregates the host set's value objects in to +// string fields delimited with the aggregateDelimiter of "|" +type hostSetAgg struct { + PublicId string `gorm:"primary_key"` + CatalogId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + Version uint32 + Attributes []byte + PreferredEndpoints string + HostIds string +} + +func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { + const op = "plugin.(hostSetAgg).toHostSet" + const aggregateDelimiter = "|" + const priorityDelimiter = "=" + hs := allocHostSet() + hs.PublicId = agg.PublicId + hs.CatalogId = agg.CatalogId + hs.PluginId = agg.PluginId + hs.Name = agg.Name + hs.Description = agg.Description + hs.CreateTime = agg.CreateTime + hs.UpdateTime = agg.UpdateTime + hs.Version = agg.Version + hs.Attributes = agg.Attributes + if agg.HostIds != "" { + hs.HostIds = strings.Split(agg.HostIds, aggregateDelimiter) + } + if agg.PreferredEndpoints != "" { + eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter) + if len(eps) > 0 { + // We want to protect against someone messing with the DB + // and not panic, so we do a bit of a dance here + var sortErr error + sort.Slice(eps, func(i, j int) bool { + epi := strings.Split(eps[i], priorityDelimiter) + if len(epi) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i])) + return false + } + epj := strings.Split(eps[j], priorityDelimiter) + if len(epj) != 2 { + sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j])) + return false + } + indexi, err := strconv.Atoi(epi[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + indexj, err := strconv.Atoi(epj[0]) + if err != nil { + sortErr = errors.Wrap(ctx, err, op) + return false + } + return indexi < indexj + }) + if sortErr != nil { + return nil, sortErr + } + for i, ep := range eps { + // At this point they're in the correct order, but we still + // have to strip off the priority + eps[i] = strings.Split(ep, priorityDelimiter)[1] + } + hs.PreferredEndpoints = eps + } + } + return hs, nil +} + +// TableName returns the table name for gorm +func (agg *hostSetAgg) TableName() string { + return "host_plugin_host_set_with_value_obj" +} diff --git a/internal/host/plugin/host_set_member_test.go b/internal/host/plugin/host_set_member_test.go index 47c67844e..270289a46 100644 --- a/internal/host/plugin/host_set_member_test.go +++ b/internal/host/plugin/host_set_member_test.go @@ -32,60 +32,74 @@ func TestHostSetMember_InsertDelete(t *testing.T) { plg.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), } + repo, err := NewRepository(rw, rw, kms, plgm) + require.NoError(t, err) + cats := TestCatalogs(t, conn, prj.PublicId, plg.PublicId, 2) blueCat := cats[0] blueSet1 := TestSet(t, conn, kms, blueCat, plgm) blueSet2 := TestSet(t, conn, kms, blueCat, plgm) + blueSet3 := TestSet(t, conn, kms, blueCat, plgm) hostId, err := db.NewPublicId(HostPrefix) require.NoError(t, err) - blueHost1 := NewHost(ctx, blueCat.PublicId, "abcd", withPluginId(plg.GetPublicId())) + blueHost1 := NewHost(ctx, blueCat.PublicId, "blue1", withPluginId(plg.GetPublicId())) blueHost1.PublicId = hostId require.NoError(t, rw.Create(ctx, blueHost1)) hostId, err = db.NewPublicId(HostPrefix) require.NoError(t, err) - blueHost2 := NewHost(ctx, blueCat.PublicId, "zyxw", withPluginId(plg.GetPublicId())) + blueHost2 := NewHost(ctx, blueCat.PublicId, "blue2", withPluginId(plg.GetPublicId())) blueHost2.PublicId = hostId require.NoError(t, rw.Create(ctx, blueHost2)) + hostId, err = db.NewPublicId(HostPrefix) + require.NoError(t, err) + blueHost3 := NewHost(ctx, blueCat.PublicId, "blue3", withPluginId(plg.GetPublicId())) + blueHost3.PublicId = hostId + require.NoError(t, rw.Create(ctx, blueHost3)) + + hostId, err = db.NewPublicId(HostPrefix) + require.NoError(t, err) + blueHost4 := NewHost(ctx, blueCat.PublicId, "blue4", withPluginId(plg.GetPublicId())) + blueHost4.PublicId = hostId + require.NoError(t, rw.Create(ctx, blueHost4)) + greenCat := cats[1] greenSet := TestSet(t, conn, kms, greenCat, plgm) tests := []struct { name string - sets []string - host *Host + set string + hosts []*Host wantErr bool direct bool }{ { - name: "valid-host-in-set", - sets: []string{blueSet1.PublicId}, - host: blueHost1, + name: "valid-host-in-set", + set: blueSet1.PublicId, + hosts: []*Host{blueHost1}, }, { - name: "valid-other-host-in-set", - sets: []string{blueSet2.PublicId}, - host: blueHost2, + name: "valid-other-host-in-set", + set: blueSet2.PublicId, + hosts: []*Host{blueHost2}, + }, + { + name: "valid-two-hosts-in-set", + set: blueSet3.PublicId, + hosts: []*Host{blueHost3, blueHost4}, }, { name: "invalid-diff-catalogs", - sets: []string{greenSet.PublicId}, - host: blueHost1, + set: greenSet.PublicId, + hosts: []*Host{blueHost1}, wantErr: true, }, { name: "test-vet-for-write-no-set", - host: blueHost1, - sets: []string{""}, - wantErr: true, - direct: true, - }, - { - name: "test-vet-for-write-no-host", - sets: []string{blueSet1.PublicId}, + hosts: []*Host{blueHost1}, wantErr: true, direct: true, }, @@ -94,19 +108,21 @@ func TestHostSetMember_InsertDelete(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - for _, set := range tt.sets { + var hostIds []string + for _, host := range tt.hosts { var got *HostSetMember var err error + hostIds = append(hostIds, host.PublicId) if !tt.direct { - got, err = NewHostSetMember(ctx, set, tt.host.PublicId) + got, err = NewHostSetMember(ctx, tt.set, host.PublicId) } else { got = &HostSetMember{ HostSetMember: &store.HostSetMember{ - SetId: set, + SetId: tt.set, }, } - if tt.host != nil { - got.HostId = tt.host.PublicId + if host != nil { + got.HostId = host.PublicId } } require.NoError(err) @@ -120,15 +136,21 @@ func TestHostSetMember_InsertDelete(t *testing.T) { } // Run a test on the aggregate to validate looking up sets - agg := &hostAgg{PublicId: tt.host.PublicId} - require.NoError(rw.LookupByPublicId(ctx, agg)) - h, err := agg.toHost(ctx) + for _, host := range tt.hosts { + agg := &hostAgg{PublicId: host.PublicId} + require.NoError(rw.LookupByPublicId(ctx, agg)) + h, err := agg.toHost(ctx) + require.NoError(err) + assert.ElementsMatch(h.SetIds, []string{tt.set}) + } + + set, _, err := repo.LookupSet(ctx, tt.set) require.NoError(err) - assert.ElementsMatch(h.SetIds, tt.sets) + require.NotNil(set) + require.ElementsMatch(set.HostIds, hostIds) }) } - repo, err := NewRepository(rw, rw, kms, plgm) - require.NoError(t, err) + hosts, err := repo.ListHostsBySetIds(ctx, []string{blueSet1.PublicId, blueSet2.PublicId}) require.NoError(t, err) require.Len(t, hosts, 2) @@ -136,7 +158,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { // Base case the count by catalog ID hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) - assert.Len(t, hosts, 2) + assert.Len(t, hosts, 4) // Delete first membership, validate host is gone got, err := NewHostSetMember(ctx, blueSet1.PublicId, blueHost1.PublicId) @@ -149,7 +171,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost1.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) - require.Len(t, hosts, 1) + require.Len(t, hosts, 3) // Delete second, validate second host is gone got, err = NewHostSetMember(ctx, blueSet2.PublicId, blueHost2.PublicId) @@ -162,6 +184,21 @@ func TestHostSetMember_InsertDelete(t *testing.T) { assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost2.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) + require.Len(t, hosts, 2) + + // Delete third set, validate remaining hosts are gone + gotSet, err := NewHostSet(ctx, blueCat.PublicId) + require.NoError(t, err) + require.NotNil(t, got) + gotSet.PublicId = blueSet3.PublicId + num, err = rw.Delete(ctx, gotSet) + require.NoError(t, err) + assert.Equal(t, 1, num) + require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost3.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) + assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost4.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) + hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + require.NoError(t, err) require.Len(t, hosts, 0) } diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index 9829982f6..e84d85702 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -16,6 +16,7 @@ type Option func(*options) // options = how options are represented type options struct { + withPublicId string withPluginId string withName string withDescription string @@ -41,6 +42,13 @@ func withPluginId(with string) Option { } } +// WithPublicId provides an optional public id. +func WithPublicId(with string) Option { + return func(o *options) { + o.withPublicId = with + } +} + // WithDescription provides an optional description. func WithDescription(desc string) Option { return func(o *options) { diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index a8c4b2b3f..fce504999 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -8,6 +8,12 @@ import ( func Test_GetOpts(t *testing.T) { t.Parallel() + t.Run("WithPublicId", func(t *testing.T) { + opts := getOpts(WithPublicId("test")) + testOpts := getDefaultOptions() + testOpts.withPublicId = "test" + assert.Equal(t, opts, testOpts) + }) t.Run("WithName", func(t *testing.T) { opts := getOpts(WithName("test")) testOpts := getDefaultOptions() diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index cf755b8b1..6f3d361f5 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -3,12 +3,8 @@ package plugin import ( "context" "fmt" - "sort" - "strconv" - "strings" "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" @@ -16,7 +12,6 @@ import ( "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" - hspb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -152,78 +147,26 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, // LookupSet will look up a host set in the repository and return the host set, // as well as host IDs that match. If the host set is not found, it will return -// nil, nil, nil, nil. Supported options: WithSetMembers, which requests that -// host IDs contained within the set are looked up and returned. (In the future -// we may make it automatic to return this if it's coming from the database.) -func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host.Option) (*HostSet, []string, *hostplugin.Plugin, error) { +// nil, nil, nil. No options are currently supported. +func (r *Repository) LookupSet(ctx context.Context, publicId string, _ ...host.Option) (*HostSet, *hostplugin.Plugin, error) { const op = "plugin.(Repository).LookupSet" if publicId == "" { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } - opts, err := host.GetOpts(opt...) + sets, plg, err := r.getSets(ctx, publicId, "") if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - - sets, plg, err := r.getSets(ctx, publicId, "", opt...) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) + return nil, nil, errors.Wrap(ctx, err, op) } switch { case len(sets) == 0: - return nil, nil, nil, nil // not an error to return no rows for a "lookup" + return nil, nil, nil // not an error to return no rows for a "lookup" case len(sets) > 1: - return nil, nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) + return nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("%s matched more than 1 ", publicId)) } - setToReturn := sets[0] - var hostIdsToReturn []string - - // FIXME: change to use the database - if plg != nil && opts.WithSetMembers { - cat, persisted, err := r.getCatalog(ctx, setToReturn.GetCatalogId()) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - plgCat, err := toPluginCatalog(ctx, cat) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - plgSet, err := toPluginSet(ctx, setToReturn) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - plgClient, ok := r.plugins[plg.GetPublicId()] - if !ok { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("no plugin found for plugin id %s", plg.GetPublicId())) - } - resp, err := plgClient.ListHosts(ctx, &plgpb.ListHostsRequest{ - Catalog: plgCat, - Sets: []*hspb.HostSet{plgSet}, - Persisted: persisted, - }) - switch { - case err != nil: - // If it's just not implemented, e.g. for tests, don't error out, return what we have - if status.Code(err) != codes.Unimplemented { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - case resp != nil: - for _, respHost := range resp.GetHosts() { - hostId, err := newHostId(ctx, setToReturn.GetCatalogId(), respHost.GetExternalId()) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - hostIdsToReturn = append(hostIdsToReturn, hostId) - } - } - } - - sort.Strings(hostIdsToReturn) - - return setToReturn, hostIdsToReturn, plg, nil + return sets[0], plg, nil } // ListSets returns a slice of HostSets for the catalogId. WithLimit is the @@ -385,80 +328,6 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str return sets, plg, nil } -// hostSetAgg is a view that aggregates the host set's value objects in to -// string fields delimited with the aggregateDelimiter of "|" -type hostSetAgg struct { - PublicId string `gorm:"primary_key"` - CatalogId string - PluginId string - Name string - Description string - CreateTime *timestamp.Timestamp - UpdateTime *timestamp.Timestamp - Version uint32 - Attributes []byte - PreferredEndpoints string -} - -func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { - const op = "plugin.(hostSetAgg).toHostSet" - const aggregateDelimiter = "|" - const priorityDelimiter = "=" - hs := allocHostSet() - hs.PublicId = agg.PublicId - hs.CatalogId = agg.CatalogId - hs.Name = agg.Name - hs.Description = agg.Description - hs.CreateTime = agg.CreateTime - hs.UpdateTime = agg.UpdateTime - hs.Version = agg.Version - hs.Attributes = agg.Attributes - if agg.PreferredEndpoints != "" { - eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter) - if len(eps) > 0 { - // We want to protect against someone messing with the DB - // and not panic, so we do a bit of a dance here - var sortErr error - sort.Slice(eps, func(i, j int) bool { - epi := strings.Split(eps[i], priorityDelimiter) - if len(epi) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i])) - return false - } - epj := strings.Split(eps[j], priorityDelimiter) - if len(epj) != 2 { - sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j])) - return false - } - indexi, err := strconv.Atoi(epi[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - indexj, err := strconv.Atoi(epj[0]) - if err != nil { - sortErr = errors.Wrap(ctx, err, op) - return false - } - return indexi < indexj - }) - if sortErr != nil { - return nil, sortErr - } - for i, ep := range eps { - // At this point they're in the correct order, but we still - // have to strip off the priority - eps[i] = strings.Split(ep, priorityDelimiter)[1] - } - hs.PreferredEndpoints = eps - } - } - return hs, nil -} - -// TableName returns the table name for gorm -func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" } - // toPluginSet returns a host set in the format expected by the host plugin system. func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { const op = "plugin.toPluginSet" diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 0a8895772..be6312a5d 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -359,7 +359,7 @@ func TestRepository_LookupSet(t *testing.T) { repo, err := NewRepository(rw, rw, kms, plgm) assert.NoError(err) require.NotNil(repo) - got, _, _, err := repo.LookupSet(ctx, tt.in) + got, _, err := repo.LookupSet(ctx, tt.in) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index cd047b20a..a56a569ec 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -423,7 +423,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.H if err != nil { return nil, nil, nil, err } - hset, hostIds, hsplg, err := repo.LookupSet(ctx, id, host.WithSetMembers(true)) + hset, hsplg, err := repo.LookupSet(ctx, id) if err != nil { return nil, nil, nil, err } @@ -432,7 +432,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.H } hs = hset plg = toPluginInfo(hsplg) - for _, h := range hostIds { + for _, h := range hset.HostIds { hl = append(hl, &plugin.Host{ Host: &plugstore.Host{ PublicId: h, @@ -690,7 +690,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty } set = ss case plugin.Subtype: - ps, _, _, err := pluginRepo.LookupSet(ctx, id) + ps, _, err := pluginRepo.LookupSet(ctx, id) if err != nil { res.Error = err return nil, res From 1312628a1ef8b4ab95e642138462112f1b178925 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 1 Nov 2021 09:07:56 -0700 Subject: [PATCH 43/71] Add plugin set sync and orphaned host cleanup jobs (#1647) --- go.mod | 1 - go.sum | 1 - .../oss/postgres/19/05_plugin_host.up.sql | 5 +- .../postgres/19/06_preferred_endpoints.up.sql | 2 + internal/host/plugin/host.go | 8 +- internal/host/plugin/host_address_test.go | 8 +- internal/host/plugin/host_set.go | 8 + internal/host/plugin/host_set_member_test.go | 29 +- internal/host/plugin/host_test.go | 12 - internal/host/plugin/immutable_fields_test.go | 4 +- .../host/plugin/job_orphaned_host_cleanup.go | 158 +++++ .../plugin/job_orphaned_host_cleanup_test.go | 161 ++++++ internal/host/plugin/job_set_sync.go | 547 ++++++++++++++++++ internal/host/plugin/job_set_sync_test.go | 291 ++++++++++ internal/host/plugin/jobs.go | 32 + internal/host/plugin/query.go | 9 + internal/host/plugin/repository.go | 13 +- internal/host/plugin/repository_host.go | 316 +--------- .../host/plugin/repository_host_catalog.go | 6 +- .../plugin/repository_host_catalog_test.go | 20 +- internal/host/plugin/repository_host_set.go | 207 ++----- .../host/plugin/repository_host_set_test.go | 72 +-- internal/host/plugin/repository_host_test.go | 16 +- internal/host/plugin/repository_test.go | 94 +-- internal/host/plugin/store/host.pb.go | 200 ++++--- internal/host/plugin/testing.go | 84 ++- internal/host/plugin/testing_test.go | 91 ++- internal/host/preferred_endpoint_test.go | 7 +- .../storage/host/plugin/store/v1/host.proto | 20 +- internal/servers/controller/controller.go | 5 +- .../host_catalog_service_test.go | 28 +- .../host_sets/host_set_service_test.go | 46 +- .../handlers/targets/target_service_test.go | 32 +- 33 files changed, 1814 insertions(+), 719 deletions(-) create mode 100644 internal/host/plugin/job_orphaned_host_cleanup.go create mode 100644 internal/host/plugin/job_orphaned_host_cleanup_test.go create mode 100644 internal/host/plugin/job_set_sync.go create mode 100644 internal/host/plugin/job_set_sync_test.go create mode 100644 internal/host/plugin/jobs.go diff --git a/go.mod b/go.mod index 2370513b1..7b1e0a1ab 100644 --- a/go.mod +++ b/go.mod @@ -140,7 +140,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.1.0 // indirect github.com/hashicorp/go-plugin v1.0.1 // indirect - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-version v1.3.0 // indirect diff --git a/go.sum b/go.sum index ad5afde4e..63f3fa19d 100644 --- a/go.sum +++ b/go.sum @@ -477,7 +477,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5-0.20211025115820-78e1ded4aea1/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= diff --git a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql index 048ab99e0..fefe23f65 100644 --- a/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/05_plugin_host.up.sql @@ -123,6 +123,8 @@ begin; description text, create_time wt_timestamp, update_time wt_timestamp, + last_sync_time wt_timestamp, + need_sync bool not null, version wt_version, attributes bytea not null, constraint host_plugin_set_catalog_id_name_uq @@ -367,6 +369,7 @@ begin; h.public_id, h.catalog_id, h.external_id, + hc.scope_id, hc.plugin_id, h.name, h.description, @@ -382,7 +385,7 @@ begin; left outer join host_ip_address hip on h.public_id = hip.host_id left outer join host_dns_name hdns on h.public_id = hdns.host_id left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id - group by h.public_id, hc.plugin_id; + group by h.public_id, hc.plugin_id, hc.scope_id; comment on view host_plugin_host_with_value_obj_and_set_memberships is 'host plugin host with its associated value objects'; diff --git a/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql index cba52df65..94c8a4ad0 100644 --- a/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/06_preferred_endpoints.up.sql @@ -58,6 +58,8 @@ create view host_plugin_host_set_with_value_obj as hs.description, hs.create_time, hs.update_time, + hs.last_sync_time, + hs.need_sync, hs.version, hs.attributes, -- the string_agg(..) column will be null if there are no associated value objects diff --git a/internal/host/plugin/host.go b/internal/host/plugin/host.go index 73db33d15..b83e9dd85 100644 --- a/internal/host/plugin/host.go +++ b/internal/host/plugin/host.go @@ -22,7 +22,7 @@ type Host struct { tableName string `gorm:"-"` } -// newHost creates a new in memory Host assigned to catalogId with an address. +// NewHost creates a new in memory Host assigned to catalogId with an address. // Supported options: WithName, WithDescription, WithIpAddresses, WithDnsNames, // WithPluginId, WithPublicId. Others ignored. func NewHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host { @@ -109,6 +109,7 @@ func (h *Host) oplog(op oplog.OpType) oplog.Metadata { type hostAgg struct { PublicId string `gorm:"primary_key"` CatalogId string + ScopeId string ExternalId string PluginId string Name string @@ -120,8 +121,7 @@ type hostAgg struct { SetIds string } -func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { - const op = "plugin.(hostAgg).toHost" +func (agg *hostAgg) toHost() *Host { const aggregateDelimiter = "|" h := allocHost() h.PublicId = agg.PublicId @@ -148,7 +148,7 @@ func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) { sort.Strings(h.SetIds) } - return h, nil + return h } // TableName returns the table name for gorm diff --git a/internal/host/plugin/host_address_test.go b/internal/host/plugin/host_address_test.go index 619075719..41704b2d3 100644 --- a/internal/host/plugin/host_address_test.go +++ b/internal/host/plugin/host_address_test.go @@ -21,7 +21,7 @@ func TestHostDnsName_Create(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - host1 := testHost(t, conn, cat.GetPublicId(), "external") + host1 := TestHost(t, conn, cat.GetPublicId(), "external") type args struct { hostId string @@ -152,7 +152,7 @@ func TestHostIpAddress_Create(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - host1 := testHost(t, conn, cat.GetPublicId(), "external") + host1 := TestHost(t, conn, cat.GetPublicId(), "external") type args struct { hostId string @@ -291,7 +291,7 @@ func TestHostDnsName_Delete(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - host1 := testHost(t, conn, cat.GetPublicId(), "external") + host1 := TestHost(t, conn, cat.GetPublicId(), "external") addr1, err := host.NewDnsName(ctx, host1.GetPublicId(), "addr1.foo.com") require.NoError(t, err) require.NoError(t, w.Create(ctx, addr1)) @@ -358,7 +358,7 @@ func TestHostIpAddress_Delete(t *testing.T) { _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplugin.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - host1 := testHost(t, conn, cat.GetPublicId(), "external") + host1 := TestHost(t, conn, cat.GetPublicId(), "external") addr1, err := host.NewIpAddress(ctx, host1.GetPublicId(), "1.2.3.4") require.NoError(t, err) require.NoError(t, w.Create(ctx, addr1)) diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index ee18fd8d9..56efc07b7 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -99,6 +99,8 @@ type hostSetAgg struct { Description string CreateTime *timestamp.Timestamp UpdateTime *timestamp.Timestamp + LastSyncTime *timestamp.Timestamp + NeedSync bool Version uint32 Attributes []byte PreferredEndpoints string @@ -117,6 +119,8 @@ func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { hs.Description = agg.Description hs.CreateTime = agg.CreateTime hs.UpdateTime = agg.UpdateTime + hs.LastSyncTime = agg.LastSyncTime + hs.NeedSync = agg.NeedSync hs.Version = agg.Version hs.Attributes = agg.Attributes if agg.HostIds != "" { @@ -165,6 +169,10 @@ func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { return hs, nil } +func (agg *hostSetAgg) GetPublicId() string { + return agg.PublicId +} + // TableName returns the table name for gorm func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" diff --git a/internal/host/plugin/host_set_member_test.go b/internal/host/plugin/host_set_member_test.go index 270289a46..2002d0360 100644 --- a/internal/host/plugin/host_set_member_test.go +++ b/internal/host/plugin/host_set_member_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,6 +26,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create") @@ -32,15 +34,15 @@ func TestHostSetMember_InsertDelete(t *testing.T) { plg.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), } - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(t, err) cats := TestCatalogs(t, conn, prj.PublicId, plg.PublicId, 2) blueCat := cats[0] - blueSet1 := TestSet(t, conn, kms, blueCat, plgm) - blueSet2 := TestSet(t, conn, kms, blueCat, plgm) - blueSet3 := TestSet(t, conn, kms, blueCat, plgm) + blueSet1 := TestSet(t, conn, kms, sched, blueCat, plgm) + blueSet2 := TestSet(t, conn, kms, sched, blueCat, plgm) + blueSet3 := TestSet(t, conn, kms, sched, blueCat, plgm) hostId, err := db.NewPublicId(HostPrefix) require.NoError(t, err) @@ -67,7 +69,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { require.NoError(t, rw.Create(ctx, blueHost4)) greenCat := cats[1] - greenSet := TestSet(t, conn, kms, greenCat, plgm) + greenSet := TestSet(t, conn, kms, sched, greenCat, plgm) tests := []struct { name string @@ -139,8 +141,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { for _, host := range tt.hosts { agg := &hostAgg{PublicId: host.PublicId} require.NoError(rw.LookupByPublicId(ctx, agg)) - h, err := agg.toHost(ctx) - require.NoError(err) + h := agg.toHost() assert.ElementsMatch(h.SetIds, []string{tt.set}) } @@ -160,6 +161,8 @@ func TestHostSetMember_InsertDelete(t *testing.T) { require.NoError(t, err) assert.Len(t, hosts, 4) + j, err := newOrphanedHostCleanupJob(ctx, rw, rw, kms) + require.NoError(t, err) // Delete first membership, validate host is gone got, err := NewHostSetMember(ctx, blueSet1.PublicId, blueHost1.PublicId) require.NoError(t, err) @@ -167,7 +170,9 @@ func TestHostSetMember_InsertDelete(t *testing.T) { num, err := rw.Delete(ctx, got) require.NoError(t, err) assert.Equal(t, 1, num) - require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + count, err := j.deleteOrphanedHosts(ctx) + require.NoError(t, err) + assert.Equal(t, 1, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost1.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) @@ -180,7 +185,9 @@ func TestHostSetMember_InsertDelete(t *testing.T) { num, err = rw.Delete(ctx, got) require.NoError(t, err) assert.Equal(t, 1, num) - require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + count, err = j.deleteOrphanedHosts(ctx) + require.NoError(t, err) + assert.Equal(t, 1, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost2.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) @@ -194,7 +201,9 @@ func TestHostSetMember_InsertDelete(t *testing.T) { num, err = rw.Delete(ctx, gotSet) require.NoError(t, err) assert.Equal(t, 1, num) - require.NoError(t, repo.DeleteOrphanedHosts(ctx, blueCat.ScopeId)) + count, err = j.deleteOrphanedHosts(ctx) + require.NoError(t, err) + assert.Equal(t, 2, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost3.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost4.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) diff --git a/internal/host/plugin/host_test.go b/internal/host/plugin/host_test.go index 3dcbcb2c1..a930a6692 100644 --- a/internal/host/plugin/host_test.go +++ b/internal/host/plugin/host_test.go @@ -202,15 +202,3 @@ func TestHost_SetTableName(t *testing.T) { }) } } - -func testHost(t *testing.T, conn *db.DB, catId, externId string) *Host { - t.Helper() - w := db.New(conn) - ctx := context.Background() - host1 := NewHost(ctx, catId, externId) - var err error - host1.PublicId, err = newHostId(ctx, catId, externId) - require.NoError(t, err) - require.NoError(t, w.Create(ctx, host1)) - return host1 -} diff --git a/internal/host/plugin/immutable_fields_test.go b/internal/host/plugin/immutable_fields_test.go index e3ea66832..924a8110c 100644 --- a/internal/host/plugin/immutable_fields_test.go +++ b/internal/host/plugin/immutable_fields_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -97,12 +98,13 @@ func TestPluginSet_ImmutableFields(t *testing.T) { w := db.New(conn) wrapper := db.TestWrapper(t) kmsCache := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := host.TestPlugin(t, conn, "test") cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceClient{ + new := TestSet(t, conn, kmsCache, sched, cat, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), }) diff --git a/internal/host/plugin/job_orphaned_host_cleanup.go b/internal/host/plugin/job_orphaned_host_cleanup.go new file mode 100644 index 000000000..ed521f4b0 --- /dev/null +++ b/internal/host/plugin/job_orphaned_host_cleanup.go @@ -0,0 +1,158 @@ +package plugin + +import ( + "context" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/scheduler" + ua "go.uber.org/atomic" +) + +const ( + orphanedHostCleanupJobName = "plugin_host_orpahend_hosts_cleanup" + orphanedHostCleanupJobRunInterval = 5 * time.Minute +) + +// OrphanedHostCleanupJob is the recurring job that syncs hosts from sets that are. +// The OrphanedHostCleanupJob is not thread safe, +// an attempt to Run the job concurrently will result in an JobAlreadyRunning error. +type OrphanedHostCleanupJob struct { + reader db.Reader + writer db.Writer + kms *kms.Kms + limit int + + running ua.Bool + numHosts int + numProcessed int +} + +// newOrphanedHostCleanupJob creates a new in-memory OrphanedHostCleanupJob. +func newOrphanedHostCleanupJob(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, _ ...Option) (*OrphanedHostCleanupJob, error) { + const op = "plugin.newOrphanedHostCleanupJob" + switch { + case r == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing db.Reader") + case w == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing db.Writer") + case kms == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing kms") + } + + return &OrphanedHostCleanupJob{ + reader: r, + writer: w, + kms: kms, + }, nil +} + +// Status returns the current status of the set sync job. Total is the total number +// of sets that are to be synced. Completed is the number of sets already synced. +func (r *OrphanedHostCleanupJob) Status() scheduler.JobStatus { + return scheduler.JobStatus{ + Completed: r.numProcessed, + Total: r.numHosts, + } +} + +// Run queries the plugin host repo hosts with no memberships, it then +// deletes those hosts. Can not be run in parallel, if +// Run is invoked while already running an error with code JobAlreadyRunning +// will be returned. +func (r *OrphanedHostCleanupJob) Run(ctx context.Context) error { + const op = "plugin.(OrphanedHostCleanupJob).Run" + if !r.running.CAS(r.running.Load(), true) { + return errors.New(ctx, errors.JobAlreadyRunning, op, "job already running") + } + defer r.running.Store(false) + + // Verify context is not done before running + if err := ctx.Err(); err != nil { + return errors.Wrap(ctx, err, op) + } + + i, err := r.deleteOrphanedHosts(ctx) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Set numProcessed and numHosts for status report + r.numProcessed, r.numHosts = i, i + return nil +} + +// NextRunIn returns the default run frequency of the cleanup job. +func (r *OrphanedHostCleanupJob) NextRunIn() (time.Duration, error) { + const op = "plugin.(OrphanedHostCleanupJob).NextRunIn" + return orphanedHostCleanupJobRunInterval, nil +} + +// Name is the unique name of the job. +func (r *OrphanedHostCleanupJob) Name() string { + return orphanedHostCleanupJobName +} + +// Description is the human readable description of the job. +func (r *OrphanedHostCleanupJob) Description() string { + return "Periodically deletes plugin based hosts which are not members of any sets." +} + +// deleteOrphanedHosts deletes any hosts that no longer belong to any set. +// WithLimit is the only option supported. No options are currently supported. +func (r *OrphanedHostCleanupJob) deleteOrphanedHosts(ctx context.Context, _ ...Option) (int, error) { + const op = "plugin.(OrphanedHostCleanupJob).deleteOrphanedHosts" + + query := ` +public_id in + (select public_id + from host_plugin_host + where public_id not in + (select host_id from host_plugin_set_member)) +` + + var hostAggs []*hostAgg + err := r.reader.SearchWhere(ctx, &hostAggs, query, nil) + switch { + case err != nil: + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + case len(hostAggs) == 0: + return db.NoRowsAffected, nil + } + + scopeToHost := make(map[string][]*Host) + for _, ha := range hostAggs { + h := allocHost() + h.PublicId = ha.PublicId + scopeToHost[ha.ScopeId] = append(scopeToHost[ha.ScopeId], h) + } + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + for scopeId, hosts := range scopeToHost { + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + for _, h := range hosts { + metadata := h.oplog(oplog.OpType_OP_TYPE_DELETE) + dHost := h.clone() + if _, err := w.Delete(ctx, dHost, db.WithOplog(oplogWrapper, metadata)); err != nil { + return errors.Wrap(ctx, err, op) + } + } + } + return nil + }) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + return len(hostAggs), nil +} diff --git a/internal/host/plugin/job_orphaned_host_cleanup_test.go b/internal/host/plugin/job_orphaned_host_cleanup_test.go new file mode 100644 index 000000000..3b36be2fb --- /dev/null +++ b/internal/host/plugin/job_orphaned_host_cleanup_test.go @@ -0,0 +1,161 @@ +package plugin + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewOrphanedHostCleanupJob(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + type args struct { + r db.Reader + w db.Writer + kms *kms.Kms + } + tests := []struct { + name string + args args + options []Option + wantErr bool + wantErrCode errors.Code + }{ + { + name: "nil reader", + args: args{ + w: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil writer", + args: args{ + r: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil kms", + args: args{ + r: rw, + w: rw, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + got, err := newOrphanedHostCleanupJob(ctx, tt.args.r, tt.args.w, tt.args.kms) + if tt.wantErr { + require.Error(err) + assert.Nil(got) + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + require.NotNil(got) + assert.Equal(tt.args.r, got.reader) + assert.Equal(tt.args.w, got.writer) + assert.Equal(tt.args.kms, got.kms) + }) + } +} + +func TestOrphanedHostCleanupJob_Run(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) + + plgServer := &TestPluginServer{} + plg := hostplg.TestPlugin(t, conn, "run") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(plgServer), + } + + r, err := newOrphanedHostCleanupJob(ctx, rw, rw, kmsCache) + require.NoError(err) + sche := scheduler.TestScheduler(t, conn, wrapper) + err = sche.RegisterJob(context.Background(), r) + require.NoError(err) + + err = r.Run(context.Background()) + require.NoError(err) + // No sets should have been synced. + assert.Equal(0, r.numProcessed) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) + set1 := TestSet(t, conn, kmsCache, sched, cat, plgm) + host1 := TestHost(t, conn, cat.GetPublicId(), "host with membership") + TestSetMembers(t, conn, set1.GetPublicId(), []*Host{host1}) + + // 1 host with no set membership + TestHost(t, conn, cat.GetPublicId(), "host2") + + // Run sync again with the newly created set + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(1, r.numHosts) + assert.Equal(1, r.numProcessed) + + // 5 host with no set membership + TestHost(t, conn, cat.GetPublicId(), "1") + TestHost(t, conn, cat.GetPublicId(), "2") + TestHost(t, conn, cat.GetPublicId(), "3") + TestHost(t, conn, cat.GetPublicId(), "4") + TestHost(t, conn, cat.GetPublicId(), "5") + + // Run sync again with the freshly synced set + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(5, r.numHosts) + assert.Equal(5, r.numProcessed) + + // Run sync again with the freshly synced set + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(0, r.numHosts) + assert.Equal(0, r.numProcessed) + + require.NoError(rw.LookupById(ctx, host1)) +} diff --git a/internal/host/plugin/job_set_sync.go b/internal/host/plugin/job_set_sync.go new file mode 100644 index 000000000..2bc54ca37 --- /dev/null +++ b/internal/host/plugin/job_set_sync.go @@ -0,0 +1,547 @@ +package plugin + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/endpoint" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/scheduler" + hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + ua "go.uber.org/atomic" +) + +const ( + setSyncJobName = "plugin_host_set_sync" + setSyncJobRunInterval = 10 * time.Minute +) + +// SetSyncJob is the recurring job that syncs hosts from sets that are. +// The SetSyncJob is not thread safe, +// an attempt to Run the job concurrently will result in an JobAlreadyRunning error. +type SetSyncJob struct { + reader db.Reader + writer db.Writer + kms *kms.Kms + plugins map[string]plgpb.HostPluginServiceClient + limit int + + running ua.Bool + numSets int + numProcessed int +} + +// newSetSyncJob creates a new in-memory SetSyncJob. +// +// WithLimit is the only supported option. +func newSetSyncJob(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) (*SetSyncJob, error) { + const op = "plugin.newSetSyncJob" + switch { + case r == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing db.Reader") + case w == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing db.Writer") + case kms == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing kms") + case len(plgm) == 0: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing plugin manager") + } + + opts := getOpts(opt...) + if opts.withLimit == 0 { + // zero signals the boundary defaults should be used. + opts.withLimit = db.DefaultLimit + } + return &SetSyncJob{ + reader: r, + writer: w, + kms: kms, + plugins: plgm, + limit: opts.withLimit, + }, nil +} + +// Status returns the current status of the set sync job. Total is the total number +// of sets that are to be synced. Completed is the number of sets already synced. +func (r *SetSyncJob) Status() scheduler.JobStatus { + return scheduler.JobStatus{ + Completed: r.numProcessed, + Total: r.numSets, + } +} + +// Run queries the plugin host repo for sets that need to be synced, it then +// creates a plugin client and syncs each set. Can not be run in parallel, if +// Run is invoked while already running an error with code JobAlreadyRunning +// will be returned. +func (r *SetSyncJob) Run(ctx context.Context) error { + const op = "plugin.(SetSyncJob).Run" + if !r.running.CAS(r.running.Load(), true) { + return errors.New(ctx, errors.JobAlreadyRunning, op, "job already running") + } + defer r.running.Store(false) + + // Verify context is not done before running + if err := ctx.Err(); err != nil { + return errors.Wrap(ctx, err, op) + } + + var setAggs []*hostSetAgg + // Fetch all sets that will reach their sync point within the syncWindow. + // This is done to avoid constantly scheduling the set sync job when there + // are multiple sets to sync in sequence. + err := r.reader.SearchWhere(ctx, &setAggs, `need_sync or last_sync_time <= wt_add_seconds_to_now(?)`, []interface{}{-1 * setSyncJobRunInterval.Seconds()}, db.WithLimit(r.limit)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Set numProcessed and numHosts for status report + r.numProcessed, r.numSets = 0, len(setAggs) + if len(setAggs) == 0 { + return nil + } + + setIds := make([]string, 0, len(setAggs)) + for _, sa := range setAggs { + setIds = append(setIds, sa.PublicId) + } + return r.syncSets(ctx, setIds) +} + +// NextRunIn queries the plugin host set db to determine when the next set should be synced. +func (r *SetSyncJob) NextRunIn() (time.Duration, error) { + const op = "plugin.(SetSyncJob).NextRunIn" + next, err := nextSync(r) + if err != nil { + return setSyncJobRunInterval, errors.WrapDeprecated(err, op) + } + return next, nil +} + +// Name is the unique name of the job. +func (r *SetSyncJob) Name() string { + return setSyncJobName +} + +// Description is the human readable description of the job. +func (r *SetSyncJob) Description() string { + return "Periodically syncs plugin based catalog hosts and host set memberships." +} + +func nextSync(j scheduler.Job) (time.Duration, error) { + const op = "plugin.nextSync" + var query string + var r db.Reader + switch job := j.(type) { + case *SetSyncJob: + query = setSyncNextRunInQuery + r = job.reader + default: + return 0, errors.NewDeprecated(errors.Unknown, op, "unknown job") + } + + rows, err := r.Query(context.Background(), query, []interface{}{setSyncJobRunInterval}) + if err != nil { + return 0, errors.WrapDeprecated(err, op) + } + defer rows.Close() + + for rows.Next() { + type NextResync struct { + SyncNow bool + ResyncIn time.Duration + } + var n NextResync + err = r.ScanRows(rows, &n) + if err != nil { + return 0, errors.WrapDeprecated(err, op) + } + if n.SyncNow || n.ResyncIn < 0 { + // If we are past the next renewal time, return 0 to schedule immediately + return 0, nil + } + return n.ResyncIn * time.Second, nil + } + return setSyncJobRunInterval, nil +} + +// syncSets retrieves from their plugins all the host and membership information +// for the provided host sets and updates their values in the database. +func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { + const op = "plugin.(Repository).Endpoints" + if len(setIds) == 0 { + return errors.New(ctx, errors.InvalidParameter, op, "no set ids") + } + + // Fist, look up the sets corresponding to the set IDs + var setAggs []*hostSetAgg + if err := r.reader.SearchWhere(ctx, &setAggs, "public_id in (?)", []interface{}{setIds}); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve sets %v", setIds))) + } + if len(setAggs) == 0 { + return nil + } + + type setInfo struct { + preferredEndpoint endpoint.Option + plgSet *pb.HostSet + } + + type catalogInfo struct { + publicId string // ID of the catalog + plg plgpb.HostPluginServiceClient // plugin client for the catalog + setInfos map[string]*setInfo // map of set IDs to set information + plgCat *hcpb.HostCatalog // plugin host catalog + storeCat *HostCatalog + persisted *plgpb.HostCatalogPersisted // host catalog persisted (secret) data + } + + // Next, look up the distinct catalog info and assign set infos to it. + // Notably, this does not include persisted info. + catalogInfos := make(map[string]*catalogInfo) + for _, ag := range setAggs { + ci, ok := catalogInfos[ag.CatalogId] + if !ok { + ci = &catalogInfo{ + publicId: ag.CatalogId, + setInfos: make(map[string]*setInfo), + } + } + ci.plg, ok = r.plugins[ag.PluginId] + if !ok { + return errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", ag.PluginId)) + } + + s, err := ag.toHostSet(ctx) + if err != nil { + return errors.Wrap(ctx, err, op) + } + si, ok := ci.setInfos[s.GetPublicId()] + if !ok { + si = &setInfo{} + } + si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints()) + si.plgSet, err = toPluginSet(ctx, s) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId()))) + } + + ci.setInfos[s.GetPublicId()] = si + catalogInfos[ag.CatalogId] = ci + } + + // Now, look up the catalog persisted (secret) information + catIds := make([]string, 0, len(catalogInfos)) + for k := range catalogInfos { + catIds = append(catIds, k) + } + var catAggs []*catalogAgg + if err := r.reader.SearchWhere(ctx, &catAggs, "public_id in (?)", []interface{}{catIds}); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve catalogs %v", catIds))) + } + if len(catAggs) == 0 { + return errors.New(ctx, errors.NotSpecificIntegrity, op, "no catalogs returned for retrieved sets") + } + for _, ca := range catAggs { + c, s := ca.toCatalogAndPersisted() + ci, ok := catalogInfos[c.GetPublicId()] + if !ok { + return errors.New(ctx, errors.NotSpecificIntegrity, op, "catalog returned when no set requested it") + } + plgCat, err := toPluginCatalog(ctx, c) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("storage to plugin catalog conversion")) + } + ci.plgCat = plgCat + ci.storeCat = c + + per, err := toPluginPersistedData(ctx, r.kms, c.GetScopeId(), s) + if err != nil { + return errors.Wrap(ctx, err, op) + } + ci.persisted = per + catalogInfos[c.GetPublicId()] = ci + } + + // For each distinct catalog, list all sets at once + for _, ci := range catalogInfos { + var sets []*pb.HostSet + var catSetIds []string + for id, si := range ci.setInfos { + sets = append(sets, si.plgSet) + catSetIds = append(catSetIds, id) + } + + resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ + Catalog: ci.plgCat, + Sets: sets, + Persisted: ci.persisted, + }) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + if _, err := r.upsertHosts(ctx, ci.storeCat, catSetIds, resp.GetHosts()); err != nil { + errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts")) + } + + updateSyncDataQuery := ` +update host_plugin_set +set + last_sync_time = current_timestamp, + need_sync = false +where public_id in (?) +` + + // update last sync time on the sets + i, err := r.writer.Exec(ctx, updateSyncDataQuery, []interface{}{catSetIds}) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("updating last sync time")) + } + if i != len(catSetIds) { + return errors.New(ctx, errors.Internal, op, "mismatched number of sets updated and synced") + } + r.numProcessed += len(catSetIds) + } + return nil +} + +// upsertHosts inserts phs into the repository or updates its current +// attributes/set memberships and returns Hosts. h is not changed. hc must +// contain a valid public ID and scope ID. Each ph in phs must not contain a +// PublicId but must contain an external ID. The PublicId is generated and +// assigned by this method. +// +// NOTE: If phs is empty, this assumes that there are simply no hosts that +// matched the given sets! Which means it will remove all hosts from the given +// sets. +func (r *SetSyncJob) upsertHosts( + ctx context.Context, + hc *HostCatalog, + setIds []string, + phs []*plgpb.ListHostsResponseHost, + _ ...Option) ([]*Host, error) { + const op = "plugin.(SetSyncJob).upsertHosts" + if phs == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil plugin hosts") + } + for _, ph := range phs { + if ph.GetExternalId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host external id") + } + } + if hc == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil host catalog") + } + if hc.GetPublicId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + } + if hc.GetScopeId() == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if setIds == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil sets") + } + if len(setIds) == 0 { // At least one must have been given to the plugin + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty sets") + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, hc.GetScopeId(), kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + // First, fetch existing hosts for the set IDs passed in, and organize them + // into a lookup map by host ID for later usage + var currentHosts []*Host + var currentHostMap map[string]*Host + { + var err error + currentHosts, err = listHostBySetIds(ctx, r.reader, setIds) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up current hosts for returned sets")) + } + + currentHostMap = make(map[string]*Host, len(currentHosts)) + for _, h := range currentHosts { + currentHostMap[h.PublicId] = h + } + } + + // Now, parse the externally defined hosts into hostInfo values, which + // stores info useful for later comparisons + newHostMap, err := createNewHostMap(ctx, hc, phs, currentHostMap) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create new host map")) + } + + var returnedHosts []*Host + // Iterate over hosts and add or update them + for _, hi := range newHostMap { + ret := hi.h.clone() + + if !hi.dirtyHost && + len(hi.ipsToAdd) == 0 && + len(hi.ipsToRemove) == 0 && + len(hi.dnsNamesToAdd) == 0 && + len(hi.dnsNamesToRemove) == 0 { + returnedHosts = append(returnedHosts, ret) + continue + } + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0) + ticket, err := w.GetTicket(hi.h) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + if hi.dirtyHost { + var hOplogMsg oplog.Message + onConflict := &db.OnConflict{ + Target: db.Constraint("host_plugin_host_pkey"), + Action: db.SetColumns([]string{"name", "description"}), + } + if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg), db.WithOnConflict(onConflict)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &hOplogMsg) + } + + // IP handling + { + if len(hi.ipsToRemove) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToRemove)) + count, err := w.DeleteItems(ctx, hi.ipsToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) + if err != nil { + return err + } + if count != len(hi.ipsToRemove) { + return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d ips from host %s, removed %d", len(hi.ipsToRemove), hi.h.PublicId, count)) + } + msgs = append(msgs, oplogMsgs...) + } + if len(hi.ipsToAdd) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToAdd)) + if err := w.CreateItems(ctx, hi.ipsToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { + return err + } + msgs = append(msgs, oplogMsgs...) + } + } + + // DNS handling + { + if len(hi.dnsNamesToRemove) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToRemove)) + count, err := w.DeleteItems(ctx, hi.dnsNamesToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) + if err != nil { + return err + } + if count != len(hi.dnsNamesToRemove) { + return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d dns names from host %s, removed %d", len(hi.dnsNamesToRemove), hi.h.PublicId, count)) + } + msgs = append(msgs, oplogMsgs...) + } + if len(hi.dnsNamesToAdd) > 0 { + oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToAdd)) + if err := w.CreateItems(ctx, hi.dnsNamesToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { + return err + } + msgs = append(msgs, oplogMsgs...) + } + } + + metadata := hi.h.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + returnedHosts = append(returnedHosts, ret) + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + } + + // Now, check set membership changes + setMembershipsToAdd, setMembershipsToRemove, allSetIds := getSetChanges(currentHostMap, newHostMap) + + // Iterate through the sets and update memberships, one transaction per set + for setId := range allSetIds { + hs, err := NewHostSet(ctx, hc.PublicId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + hs.PublicId = setId + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0) + + ticket, err := w.GetTicket(hs) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + // Perform additions + for _, hostId := range setMembershipsToAdd[hs.PublicId] { + membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + var hOplogMsg oplog.Message + if err := w.Create(ctx, membership, db.NewOplogMsg(&hOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &hOplogMsg) + } + + // Perform removals + for _, hostId := range setMembershipsToRemove[hs.PublicId] { + membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + var hOplogMsg oplog.Message + rows, err := w.Delete(ctx, membership, db.NewOplogMsg(&hOplogMsg)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rows != 1 { + return errors.New(ctx, errors.RecordNotFound, op, "record not found when deleting set membership") + } + msgs = append(msgs, &hOplogMsg) + } + + metadata := hc.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + } + + return returnedHosts, nil +} diff --git a/internal/host/plugin/job_set_sync_test.go b/internal/host/plugin/job_set_sync_test.go new file mode 100644 index 000000000..5eb3cb510 --- /dev/null +++ b/internal/host/plugin/job_set_sync_test.go @@ -0,0 +1,291 @@ +package plugin + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewSetSyncJob(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + plg := hostplg.TestPlugin(t, conn, "lookup") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), + } + + type args struct { + r db.Reader + w db.Writer + kms *kms.Kms + plgm map[string]plgpb.HostPluginServiceClient + } + tests := []struct { + name string + args args + options []Option + wantLimit int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "nil reader", + args: args{ + w: rw, + kms: kmsCache, + plgm: plgm, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil writer", + args: args{ + r: rw, + kms: kmsCache, + plgm: plgm, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil kms", + args: args{ + r: rw, + w: rw, + plgm: plgm, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil plgm", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid-no-options", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + plgm: plgm, + }, + wantLimit: db.DefaultLimit, + }, + { + name: "valid-with-limit", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + plgm: plgm, + }, + options: []Option{WithLimit(100)}, + wantLimit: 100, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + got, err := newSetSyncJob(ctx, tt.args.r, tt.args.w, tt.args.kms, tt.args.plgm, tt.options...) + if tt.wantErr { + require.Error(err) + assert.Nil(got) + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + require.NotNil(got) + assert.Equal(tt.args.r, got.reader) + assert.Equal(tt.args.w, got.writer) + assert.Equal(tt.args.kms, got.kms) + assert.Equal(tt.wantLimit, got.limit) + }) + } +} + +func TestSetSyncJob_Run(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) + + plgServer := &TestPluginServer{} + plg := hostplg.TestPlugin(t, conn, "run") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(plgServer), + } + + r, err := newSetSyncJob(ctx, rw, rw, kmsCache, plgm) + require.NoError(err) + sche := scheduler.TestScheduler(t, conn, wrapper) + err = sche.RegisterJob(context.Background(), r) + require.NoError(err) + + err = r.Run(context.Background()) + require.NoError(err) + // No sets should have been synced. + assert.Equal(0, r.numProcessed) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + cat := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) + set1 := TestSet(t, conn, kmsCache, sched, cat, plgm) + plgServer.ListHostsFn = func(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + assert.Len(req.GetSets(), 1) + assert.Equal(set1.GetPublicId(), req.GetSets()[0].GetId()) + return &plgpb.ListHostsResponse{ + Hosts: []*plgpb.ListHostsResponseHost{ + { + ExternalId: "first", + IpAddresses: []string{"10.0.0.1"}, + SetIds: []string{req.GetSets()[0].GetId()}, + }, + }, + }, nil + } + + hsa := &hostSetAgg{PublicId: set1.GetPublicId()} + require.NoError(rw.LookupByPublicId(ctx, hsa)) + assert.Less(hsa.LastSyncTime.AsTime().UnixNano(), hsa.CreateTime.AsTime().UnixNano()) + + // Run sync again with the newly created set + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(1, r.numSets) + assert.Equal(1, r.numProcessed) + + require.NoError(rw.LookupByPublicId(ctx, hsa)) + assert.Greater(hsa.LastSyncTime.AsTime().UnixNano(), hsa.CreateTime.AsTime().UnixNano()) + assert.False(hsa.NeedSync) + firstSyncTime := hsa.LastSyncTime + + // Run sync again with the freshly synced set + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(0, r.numSets) + assert.Equal(0, r.numProcessed) + + // Set needs update + hs, err := hsa.toHostSet(ctx) + require.NoError(err) + hs.NeedSync = true + count, err := rw.Update(ctx, hs, []string{"NeedSync"}, nil) + require.NoError(err) + assert.Equal(1, count) + assert.True(hs.NeedSync) + + // Run sync again with the set needing update + err = r.Run(context.Background()) + require.NoError(err) + // The single existing set should have been processed + assert.Equal(1, r.numSets) + assert.Equal(1, r.numProcessed) + + require.NoError(rw.LookupByPublicId(ctx, hs)) + assert.Greater(hs.GetLastSyncTime().AsTime().UnixNano(), firstSyncTime.AsTime().UnixNano()) + assert.False(hs.GetNeedSync()) +} + +func TestSetSyncJob_NextRunIn(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) + kmsCache := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj := iam.TestScopes(t, iamRepo) + plg := hostplg.TestPlugin(t, conn, "lookup") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), + } + catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) + hostSet := TestSet(t, conn, kmsCache, sched, catalog, plgm) + + type setArgs struct { + lastSyncTime *timestamp.Timestamp + needsSync bool + } + tests := []struct { + name string + syncInterval time.Duration + setArgs setArgs + want time.Duration + }{ + { + name: "never-synced-before", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: false, + }, + want: 0, + }, + { + name: "synced-just-now", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: false, + }, + want: setSyncJobRunInterval, + }, + { + name: "synced-just-now-need-sync", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + want: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + r, err := newSetSyncJob(ctx, rw, rw, kmsCache, plgm) + assert.NoError(err) + require.NotNil(r) + + hostSet.NeedSync = tt.setArgs.needsSync + hostSet.LastSyncTime = tt.setArgs.lastSyncTime + _, err = rw.Update(ctx, hostSet, []string{"LastSyncTime", "NeedSync"}, nil) + require.NoError(err) + + got, err := r.NextRunIn() + require.NoError(err) + // Round to time.Minute to account for lost time between updating set and determining next run + assert.Equal(tt.want.Round(time.Minute), got.Round(time.Minute)) + }) + } +} diff --git a/internal/host/plugin/jobs.go b/internal/host/plugin/jobs.go new file mode 100644 index 000000000..3666d694d --- /dev/null +++ b/internal/host/plugin/jobs.go @@ -0,0 +1,32 @@ +package plugin + +import ( + "context" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/scheduler" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" +) + +// RegisterJobs registers plugin host related jobs with the provided scheduler. +func RegisterJobs(ctx context.Context, scheduler *scheduler.Scheduler, r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient) error { + const op = "plugin.RegisterJobs" + setSyncJob, err := newSetSyncJob(ctx, r, w, kms, plgm) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if err = scheduler.RegisterJob(ctx, setSyncJob); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("set sync job")) + } + orphanedHostCleanupJob, err := newOrphanedHostCleanupJob(ctx, r, w, kms) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if err = scheduler.RegisterJob(ctx, orphanedHostCleanupJob); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("orphaned host cleanup job")) + } + + return nil +} diff --git a/internal/host/plugin/query.go b/internal/host/plugin/query.go index 4ad81ad1a..792f4b4ab 100644 --- a/internal/host/plugin/query.go +++ b/internal/host/plugin/query.go @@ -15,5 +15,14 @@ returning *; deleteHostCatalogSecretQuery = ` delete from host_plugin_catalog_secret where catalog_id = @catalog_id; +` + + setSyncNextRunInQuery = ` +select + need_sync as sync_now, + extract(epoch from (least(now(), last_sync_time) + ?) - now())::int as resync_in +from host_plugin_set +order by need_sync desc, last_sync_time desc +limit 1; ` ) diff --git a/internal/host/plugin/repository.go b/internal/host/plugin/repository.go index 9ebf661b1..7c0f4d885 100644 --- a/internal/host/plugin/repository.go +++ b/internal/host/plugin/repository.go @@ -8,15 +8,17 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" ) // A Repository stores and retrieves the persistent types in the plugin // package. It is not safe to use a repository concurrently. type Repository struct { - reader db.Reader - writer db.Writer - kms *kms.Kms + reader db.Reader + writer db.Writer + kms *kms.Kms + scheduler *scheduler.Scheduler // plugins is a map from plugin resource id to host plugin client. plugins map[string]plgpb.HostPluginServiceClient @@ -29,7 +31,7 @@ type Repository struct { // only be used for one transaction and it is not safe for concurrent go // routines to access it. WithLimit option is used as a repo wide default // limit applied to all ListX methods. -func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient, opt ...host.Option) (*Repository, error) { +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, sched *scheduler.Scheduler, plgm map[string]plgpb.HostPluginServiceClient, opt ...host.Option) (*Repository, error) { const op = "plugin.NewRepository" switch { case r == nil: @@ -38,6 +40,8 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer") case kms == nil: return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms") + case sched == nil: + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "scheduler") case plgm == nil: return nil, errors.NewDeprecated(errors.InvalidParameter, op, "plgm") } @@ -60,6 +64,7 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb reader: r, writer: w, kms: kms, + scheduler: sched, plugins: plgs, defaultLimit: opts.WithLimit, }, nil diff --git a/internal/host/plugin/repository_host.go b/internal/host/plugin/repository_host.go index 938d6c6a9..44649f915 100644 --- a/internal/host/plugin/repository_host.go +++ b/internal/host/plugin/repository_host.go @@ -6,245 +6,8 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/oplog" - plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" ) -// UpsertHost inserts phs into the repository or updates its current -// attributes/set memberships and returns Hosts. h is not changed. hc must -// contain a valid public ID and scope ID. Each ph in phs must not contain a -// PublicId but must contain an external ID. The PublicId is generated and -// assigned by this method. -// -// NOTE: If phs is empty, this assumes that there are simply no hosts that -// matched the given sets! Which means it will remove all hosts from the given -// sets. -func (r *Repository) UpsertHosts( - ctx context.Context, - hc *HostCatalog, - setIds []string, - phs []*plgpb.ListHostsResponseHost, - _ ...Option) ([]*Host, error) { - const op = "plugin.(Repository).UpsertHosts" - if phs == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil plugin hosts") - } - for _, ph := range phs { - if ph.GetExternalId() == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host external id") - } - } - if hc == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil host catalog") - } - if hc.GetPublicId() == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") - } - if hc.GetScopeId() == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") - } - if setIds == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, "nil sets") - } - if len(setIds) == 0 { // At least one must have been given to the plugin - return nil, errors.New(ctx, errors.InvalidParameter, op, "empty sets") - } - - oplogWrapper, err := r.kms.GetWrapper(ctx, hc.GetScopeId(), kms.KeyPurposeOplog) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) - } - - // First, fetch existing hosts for the set IDs passed in, and organize them - // into a lookup map by host ID for later usage - var currentHosts []*Host - var currentHostMap map[string]*Host - { - var err error - currentHosts, err = r.ListHostsBySetIds(ctx, setIds) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up current hosts for returned sets")) - } - - currentHostMap = make(map[string]*Host, len(currentHosts)) - for _, h := range currentHosts { - currentHostMap[h.PublicId] = h - } - } - - // Now, parse the externally defined hosts into hostInfo values, which - // stores info useful for later comparisons - newHostMap, err := createNewHostMap(ctx, hc, phs, currentHostMap) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create new host map")) - } - - var returnedHosts []*Host - // Iterate over hosts and add or update them - for _, hi := range newHostMap { - ret := hi.h.clone() - - if !hi.dirtyHost && - len(hi.ipsToAdd) == 0 && - len(hi.ipsToRemove) == 0 && - len(hi.dnsNamesToAdd) == 0 && - len(hi.dnsNamesToRemove) == 0 { - returnedHosts = append(returnedHosts, ret) - continue - } - _, err = r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(r db.Reader, w db.Writer) error { - msgs := make([]*oplog.Message, 0) - ticket, err := w.GetTicket(hi.h) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) - } - - if hi.dirtyHost { - var hOplogMsg oplog.Message - onConflict := &db.OnConflict{ - Target: db.Constraint("host_plugin_host_pkey"), - Action: db.SetColumns([]string{"name", "description"}), - } - if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg), db.WithOnConflict(onConflict)); err != nil { - return errors.Wrap(ctx, err, op) - } - msgs = append(msgs, &hOplogMsg) - } - - // IP handling - { - if len(hi.ipsToRemove) > 0 { - oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToRemove)) - count, err := w.DeleteItems(ctx, hi.ipsToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) - if err != nil { - return err - } - if count != len(hi.ipsToRemove) { - return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d ips from host %s, removed %d", len(hi.ipsToRemove), hi.h.PublicId, count)) - } - msgs = append(msgs, oplogMsgs...) - } - if len(hi.ipsToAdd) > 0 { - oplogMsgs := make([]*oplog.Message, 0, len(hi.ipsToAdd)) - if err := w.CreateItems(ctx, hi.ipsToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { - return err - } - msgs = append(msgs, oplogMsgs...) - } - } - - // DNS handling - { - if len(hi.dnsNamesToRemove) > 0 { - oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToRemove)) - count, err := w.DeleteItems(ctx, hi.dnsNamesToRemove.toArray(), db.NewOplogMsgs(&oplogMsgs)) - if err != nil { - return err - } - if count != len(hi.dnsNamesToRemove) { - return errors.New(ctx, errors.UnexpectedRowsAffected, op, fmt.Sprintf("expected to remove %d dns names from host %s, removed %d", len(hi.dnsNamesToRemove), hi.h.PublicId, count)) - } - msgs = append(msgs, oplogMsgs...) - } - if len(hi.dnsNamesToAdd) > 0 { - oplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNamesToAdd)) - if err := w.CreateItems(ctx, hi.dnsNamesToAdd.toArray(), db.NewOplogMsgs(&oplogMsgs)); err != nil { - return err - } - msgs = append(msgs, oplogMsgs...) - } - } - - metadata := hi.h.oplog(oplog.OpType_OP_TYPE_UPDATE) - if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) - } - - returnedHosts = append(returnedHosts, ret) - return nil - }, - ) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - } - - // Now, check set membership changes - setMembershipsToAdd, setMembershipsToRemove, allSetIds := getSetChanges(currentHostMap, newHostMap) - - // Iterate through the sets and update memberships, one transaction per set - for setId := range allSetIds { - hs, err := NewHostSet(ctx, hc.PublicId) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - hs.PublicId = setId - - _, err = r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(r db.Reader, w db.Writer) error { - msgs := make([]*oplog.Message, 0) - - ticket, err := w.GetTicket(hs) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) - } - - // Perform additions - for _, hostId := range setMembershipsToAdd[hs.PublicId] { - membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) - if err != nil { - return errors.Wrap(ctx, err, op) - } - - var hOplogMsg oplog.Message - if err := w.Create(ctx, membership, db.NewOplogMsg(&hOplogMsg)); err != nil { - return errors.Wrap(ctx, err, op) - } - msgs = append(msgs, &hOplogMsg) - } - - // Perform removals - for _, hostId := range setMembershipsToRemove[hs.PublicId] { - membership, err := NewHostSetMember(ctx, hs.PublicId, hostId) - if err != nil { - return errors.Wrap(ctx, err, op) - } - - var hOplogMsg oplog.Message - rows, err := w.Delete(ctx, membership, db.NewOplogMsg(&hOplogMsg)) - if err != nil { - return errors.Wrap(ctx, err, op) - } - if rows != 1 { - return errors.New(ctx, errors.RecordNotFound, op, "record not found when deleting set membership") - } - msgs = append(msgs, &hOplogMsg) - } - - metadata := hc.oplog(oplog.OpType_OP_TYPE_UPDATE) - if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) - } - - return nil - }, - ) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - } - - return returnedHosts, nil -} - // LookupHost will look up a host in the repository. If the host is not // found, it will return nil, nil. All options are ignored. func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, error) { @@ -261,11 +24,7 @@ func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Opt } return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) } - h, err := ha.toHost(ctx) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed to convert host agg for %s", publicId))) - } - return h, nil + return ha.toHost(), nil } // ListHostsByCatalogId returns a slice of Hosts for the catalogId. @@ -293,11 +52,7 @@ func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, hosts := make([]*Host, 0, len(hostAggs)) for _, ha := range hostAggs { - host, err := ha.toHost(ctx) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - hosts = append(hosts, host) + hosts = append(hosts, ha.toHost()) } return hosts, nil @@ -316,7 +71,16 @@ func (r *Repository) ListHostsBySetIds(ctx context.Context, setIds []string, opt // non-zero signals an override of the default limit for the repo. limit = opts.withLimit } + hs, err := listHostBySetIds(ctx, r.reader, setIds, WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return hs, nil +} +func listHostBySetIds(ctx context.Context, reader db.Reader, setIds []string, opt ...Option) ([]*Host, error) { + const op = "plugin.listHostsBySetIds" + opts := getOpts(opt...) query := ` public_id in (select distinct host_id @@ -325,7 +89,7 @@ public_id in ` var hostAggs []*hostAgg - err := r.reader.SearchWhere(ctx, &hostAggs, query, []interface{}{setIds}, db.WithLimit(limit)) + err := reader.SearchWhere(ctx, &hostAggs, query, []interface{}{setIds}, db.WithLimit(opts.withLimit)) switch { case err != nil: @@ -336,62 +100,8 @@ public_id in hosts := make([]*Host, 0, len(hostAggs)) for _, ha := range hostAggs { - host, err := ha.toHost(ctx) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - hosts = append(hosts, host) + hosts = append(hosts, ha.toHost()) } return hosts, nil } - -// DeleteOphanedHosts deletes any hosts that no longer belong to any set. -// WithLimit is the only option supported. No options are currently supported. -func (r *Repository) DeleteOrphanedHosts(ctx context.Context, scopeId string, _ ...Option) error { - const op = "plugin.(Repository).DeleteOrphanedHosts" - - oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) - } - - query := ` -public_id in - (select public_id - from host_plugin_host - where public_id not in - (select host_id from host_plugin_set_member)) -` - - var hostAggs []*hostAgg - err = r.reader.SearchWhere(ctx, &hostAggs, query, nil) - switch { - case err != nil: - return errors.Wrap(ctx, err, op) - case len(hostAggs) == 0: - return nil - } - - _, err = r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(r db.Reader, w db.Writer) error { - for _, ha := range hostAggs { - h := NewHost(ctx, ha.CatalogId, ha.ExternalId) - h.PublicId = ha.PublicId - metadata := h.oplog(oplog.OpType_OP_TYPE_DELETE) - dHost := h.clone() - if _, err := w.Delete(ctx, dHost, db.WithOplog(oplogWrapper, metadata)); err != nil { - return errors.Wrap(ctx, err, op) - } - } - return nil - }) - if err != nil { - return errors.Wrap(ctx, err, op) - } - - return nil -} diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index aba27864a..40aa5302c 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -290,7 +290,7 @@ func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, * var p *plgpb.HostCatalogPersisted if s != nil { var err error - p, err = r.toPluginPersistedData(ctx, c.GetScopeId(), s) + p, err = toPluginPersistedData(ctx, r.kms, c.GetScopeId(), s) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) } @@ -337,7 +337,7 @@ func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, err // toPluginCatalog converts a *HostCatalogSecret from storage to a // *plgpb.HostCatalogPersisted expected by a plugin. Scope Id must be set. -func (r *Repository) toPluginPersistedData(ctx context.Context, scopeId string, cSecret *HostCatalogSecret) (*plgpb.HostCatalogPersisted, error) { +func toPluginPersistedData(ctx context.Context, kmsCache *kms.Kms, scopeId string, cSecret *HostCatalogSecret) (*plgpb.HostCatalogPersisted, error) { const op = "plugin.(Repository).getPersistedDataForCatalog" if scopeId == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "empty scope id") @@ -345,7 +345,7 @@ func (r *Repository) toPluginPersistedData(ctx context.Context, scopeId string, if cSecret == nil { return nil, nil } - dbWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + dbWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) } diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 5ea129d90..e8ac5db4d 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,6 +29,7 @@ func TestRepository_CreateCatalog(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test") unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") @@ -250,7 +252,7 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kmsCache := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kmsCache, plgm) + repo, err := NewRepository(rw, rw, kmsCache, sched, plgm) assert.NoError(err) assert.NotNil(repo) got, _, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) @@ -301,7 +303,7 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run("invalid-duplicate-names", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -331,7 +333,7 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -379,6 +381,7 @@ func TestRepository_LookupCatalog(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test") plgm := map[string]plgpb.HostPluginServiceClient{ @@ -418,7 +421,7 @@ func TestRepository_LookupCatalog(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) @@ -446,11 +449,12 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) { wrapper := db.TestWrapper(t) rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) plg := hostplg.TestPlugin(t, conn, "test") plgm := map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): &WrappingPluginClient{Server: &TestPluginServer{}}, } - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(t, err) assert.NotNil(t, repo) @@ -477,6 +481,7 @@ func TestRepository_DeleteCatalog(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test") pluginInstance := &TestPluginServer{} @@ -490,7 +495,7 @@ func TestRepository_DeleteCatalog(t *testing.T) { assert.NotNil(t, badId) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(t, err) assert.NotNil(t, repo) @@ -562,6 +567,7 @@ func TestRepository_DeleteCatalogX(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "test") plgm := map[string]plgpb.HostPluginServiceClient{ @@ -601,7 +607,7 @@ func TestRepository_DeleteCatalogX(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 6f3d361f5..3c14e0178 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -3,15 +3,16 @@ package plugin import ( "context" "fmt" + "time" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" - hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" @@ -58,6 +59,8 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return nil, nil, errors.Wrap(ctx, err, op) } s.PublicId = id + s.LastSyncTime = timestamp.New(time.Unix(0, 0)) + s.NeedSync = true plgClient, ok := r.plugins[c.GetPluginId()] if !ok || plgClient == nil { @@ -138,6 +141,10 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId))) } + + // The set now exists in the plugin, sync it immediately. + r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0) + plg, err := r.getPlugin(ctx, c.GetPluginId()) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) @@ -365,171 +372,69 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En if len(setAggs) == 0 { return nil, nil } - - type setInfo struct { - preferredEndpoint endpoint.Option - plgSet *pb.HostSet - } - - type catalogInfo struct { - publicId string // ID of the catalog - plg plgpb.HostPluginServiceClient // plugin client for the catalog - setInfos map[string]*setInfo // map of set IDs to set information - plgCat *hcpb.HostCatalog // storage host catalog - persisted *plgpb.HostCatalogPersisted // host catalog persisted (secret) data - } - - // Next, look up the distinct catalog info and assign set infos to it. - // Notably, this does not include persisted info. - catalogInfos := make(map[string]*catalogInfo) - for _, ag := range setAggs { - ci, ok := catalogInfos[ag.CatalogId] - if !ok { - ci = &catalogInfo{ - publicId: ag.CatalogId, - setInfos: make(map[string]*setInfo), - } - } - ci.plg, ok = r.plugins[ag.PluginId] - if !ok { - return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", ag.PluginId)) - } - - s, err := ag.toHostSet(ctx) + setIdToSet := make(map[string]*HostSet, len(setAggs)) + for _, s := range setAggs { + var err error + setIdToSet[s.PublicId], err = s.toHostSet(ctx) if err != nil { return nil, errors.Wrap(ctx, err, op) } - si, ok := ci.setInfos[s.GetPublicId()] - if !ok { - si = &setInfo{} - } - si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints()) - si.plgSet, err = toPluginSet(ctx, s) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId()))) - } - - ci.setInfos[s.GetPublicId()] = si - catalogInfos[ag.CatalogId] = ci } - // Now, look up the catalog persisted (secret) information - catIds := make([]string, 0, len(catalogInfos)) - for k := range catalogInfos { - catIds = append(catIds, k) + var setMembers []*HostSetMember + if err := r.reader.SearchWhere(ctx, &setMembers, "set_id in (?)", []interface{}{setIds}); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve set members for sets %v", setIds))) } - var catAggs []*catalogAgg - if err := r.reader.SearchWhere(ctx, &catAggs, "public_id in (?)", []interface{}{catIds}); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve catalogs %v", catIds))) - } - if len(catAggs) == 0 { - return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "no catalogs returned for retrieved sets") - } - for _, ca := range catAggs { - c, s := ca.toCatalogAndPersisted() - ci, ok := catalogInfos[c.GetPublicId()] - if !ok { - return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "catalog returned when no set requested it") - } - plgCat, err := toPluginCatalog(ctx, c) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("storage to plugin catalog conversion")) - } - ci.plgCat = plgCat - - per, err := r.toPluginPersistedData(ctx, c.GetScopeId(), s) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - ci.persisted = per - catalogInfos[c.GetPublicId()] = ci + if len(setMembers) == 0 { + return nil, nil + } + + hostIdToSetIds := make(map[string][]string) + for _, m := range setMembers { + hostIdToSetIds[m.GetHostId()] = append(hostIdToSetIds[m.GetHostId()], m.GetSetId()) + } + var hostIds []string + for hid := range hostIdToSetIds { + hostIds = append(hostIds, hid) + } + var hostAggs []*hostAgg + if err := r.reader.SearchWhere(ctx, &hostAggs, "public_id in (?)", []interface{}{hostIds}); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve hosts %v", hostIds))) + } + if len(hostAggs) == 0 { + return nil, nil } - // For writing the hosts to the db so data warehouse doesn't complain - var hosts []interface{} - hostIds := map[string]bool{} var es []*host.Endpoint - - // For each distinct catalog, list all sets at once - for _, ci := range catalogInfos { - - var sets []*pb.HostSet - for _, si := range ci.setInfos { - sets = append(sets, si.plgSet) - } - - resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{ - Catalog: ci.plgCat, - Sets: sets, - Persisted: ci.persisted, - }) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - - for _, h := range resp.GetHosts() { - hostId, err := newHostId(ctx, ci.publicId, h.GetExternalId()) + for _, ha := range hostAggs { + h := ha.toHost() + for _, sId := range hostIdToSetIds[h.GetPublicId()] { + s := setIdToSet[sId] + pref, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(s.GetPreferredEndpoints())) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId))) + } + var opts []endpoint.Option + if len(h.GetIpAddresses()) > 0 { + opts = append(opts, endpoint.WithIpAddrs(h.GetIpAddresses())) + } + if len(h.GetDnsNames()) > 0 { + opts = append(opts, endpoint.WithIpAddrs(h.GetDnsNames())) + } + addr, err := pref.Choose(ctx, opts...) if err != nil { return nil, errors.Wrap(ctx, err, op) } - - for _, sId := range h.GetSetIds() { - var opts []endpoint.Option - if len(h.GetIpAddresses()) > 0 { - opts = append(opts, endpoint.WithIpAddrs(h.GetIpAddresses())) - } - if len(h.GetDnsNames()) > 0 { - opts = append(opts, endpoint.WithIpAddrs(h.GetDnsNames())) - } - - si, ok := ci.setInfos[sId] - if !ok { - return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "host is reporting it's part of a set we didn't query for") - } - pref, err := endpoint.NewPreferencer(ctx, si.preferredEndpoint) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId))) - } - addr, err := pref.Choose(ctx, opts...) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - if addr == "" { - continue - } - es = append(es, &host.Endpoint{ - HostId: hostId, - SetId: sId, - Address: addr, - }) - - if _, ok := hostIds[hostId]; !ok { - hostIds[hostId] = true - host := NewHost(ctx, ci.publicId, addr) - host.PublicId = hostId - hosts = append(hosts, host) - } + if addr == "" { + continue } + es = append(es, &host.Endpoint{ + HostId: h.GetPublicId(), + SetId: sId, + Address: addr, + }) } } - if len(hosts) > 0 { - _, err := r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(_ db.Reader, w db.Writer) error { - if _, err := r.writer.DeleteItems(ctx, hosts); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("couldn't delete existing hosts")) - } - if err := r.writer.CreateItems(ctx, hosts); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("can't persist hosts")) - } - return nil - }) - if err != nil { - return nil, err - } - } return es, nil } diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index be6312a5d..34fcdfbb8 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,6 +33,7 @@ func TestRepository_CreateSet(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "create") unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") @@ -208,7 +210,7 @@ func TestRepository_CreateSet(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) got, plgInfo, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) @@ -236,7 +238,7 @@ func TestRepository_CreateSet(t *testing.T) { t.Run("invalid-duplicate-names", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) @@ -267,7 +269,7 @@ func TestRepository_CreateSet(t *testing.T) { t.Run("valid-duplicate-names-diff-catalogs", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) @@ -312,6 +314,7 @@ func TestRepository_LookupSet(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "lookup") plgm := map[string]plgpb.HostPluginServiceClient{ @@ -319,7 +322,7 @@ func TestRepository_LookupSet(t *testing.T) { } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceClient{ + hostSet := TestSet(t, conn, kms, sched, catalog, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{ ListHostsFn: func(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { require.NotEmpty(t, req.GetSets()) @@ -356,7 +359,7 @@ func TestRepository_LookupSet(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) require.NotNil(repo) got, _, err := repo.LookupSet(ctx, tt.in) @@ -380,37 +383,25 @@ func TestRepository_Endpoints(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "endpoints") hostlessCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) plgm := map[string]plgpb.HostPluginServiceClient{ - plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{ - ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { - if req.Catalog.GetId() == hostlessCatalog.GetPublicId() { - return &plgpb.ListHostsResponse{}, nil - } - var setIds []string - for _, set := range req.GetSets() { - setIds = append(setIds, set.GetId()) - } - return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ - { - SetIds: setIds, - ExternalId: "test", - IpAddresses: []string{"10.0.0.5", "192.168.0.5"}, - DnsNames: nil, - }, - }}, nil - }, - }), + plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{}), } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet10 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) - hostSet192 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) - hostSet100 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) - hostlessSet := TestSet(t, conn, kms, hostlessCatalog, plgm) + hostSet10 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) + hostSet192 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) + hostSet100 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) + hostlessSet := TestSet(t, conn, kms, sched, hostlessCatalog, plgm) + + h1 := TestHost(t, conn, catalog.GetPublicId(), "test", withIpAddresses([]string{"10.0.0.5", "192.168.0.5"})) + TestSetMembers(t, conn, hostSet10.GetPublicId(), []*Host{h1}) + TestSetMembers(t, conn, hostSet192.GetPublicId(), []*Host{h1}) + TestSetMembers(t, conn, hostSet100.GetPublicId(), []*Host{h1}) tests := []struct { name string @@ -468,7 +459,7 @@ func TestRepository_Endpoints(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) require.NotNil(repo) got, err := repo.Endpoints(ctx, tt.setIds) @@ -511,7 +502,7 @@ func TestRepository_ListSets(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) - + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "list") plgm := map[string]plgpb.HostPluginServiceClient{ @@ -521,9 +512,9 @@ func TestRepository_ListSets(t *testing.T) { catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) hostSets := []*HostSet{ - TestSet(t, conn, kms, catalogA, plgm), - TestSet(t, conn, kms, catalogA, plgm), - TestSet(t, conn, kms, catalogA, plgm), + TestSet(t, conn, kms, sched, catalogA, plgm), + TestSet(t, conn, kms, sched, catalogA, plgm), + TestSet(t, conn, kms, sched, catalogA, plgm), } printoutTable(t, rw) @@ -555,7 +546,7 @@ func TestRepository_ListSets(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) require.NotNil(repo) got, gotPlg, err := repo.ListSets(context.Background(), tt.in, tt.opts...) @@ -583,7 +574,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) - + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "listlimit") plgm := map[string]plgpb.HostPluginServiceClient{ @@ -593,7 +584,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { count := 10 var hostSets []*HostSet for i := 0; i < count; i++ { - hostSets = append(hostSets, TestSet(t, conn, kms, catalog, plgm)) + hostSets = append(hostSets, TestSet(t, conn, kms, sched, catalog, plgm)) } tests := []struct { @@ -644,7 +635,7 @@ func TestRepository_ListSets_Limits(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm, tt.repoOpts...) + repo, err := NewRepository(rw, rw, kms, sched, plgm, tt.repoOpts...) assert.NoError(err) require.NotNil(repo) got, gotPlg, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...) @@ -662,6 +653,7 @@ func TestRepository_DeleteSet(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) plg := hostplg.TestPlugin(t, conn, "create") @@ -671,8 +663,8 @@ func TestRepository_DeleteSet(t *testing.T) { }}), } c := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet := TestSet(t, conn, kms, c, plgm) - hostSet2 := TestSet(t, conn, kms, c, plgm) + hostSet := TestSet(t, conn, kms, sched, c, plgm) + hostSet2 := TestSet(t, conn, kms, sched, c, plgm) newHostSetId, err := newHostSetId(ctx) require.NoError(t, err) @@ -726,7 +718,7 @@ func TestRepository_DeleteSet(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) require.NotNil(repo) got, err := repo.DeleteSet(ctx, prj.PublicId, tt.in) diff --git a/internal/host/plugin/repository_host_test.go b/internal/host/plugin/repository_host_test.go index 460192d0e..63075a98c 100644 --- a/internal/host/plugin/repository_host_test.go +++ b/internal/host/plugin/repository_host_test.go @@ -17,16 +17,18 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestRepository_UpsertHosts(t *testing.T) { +func TestJob_UpsertHosts(t *testing.T) { ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, prj := iam.TestScopes(t, iamRepo) @@ -40,7 +42,7 @@ func TestRepository_UpsertHosts(t *testing.T) { const setCount int = 3 setIds := make([]string, 0, setCount) for i := 0; i < setCount; i++ { - set := TestSet(t, conn, kms, catalog, plgm) + set := TestSet(t, conn, kms, sched, catalog, plgm) setIds = append(setIds, set.GetPublicId()) } sort.Strings(setIds) @@ -186,11 +188,11 @@ func TestRepository_UpsertHosts(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - repo, err := NewRepository(rw, rw, kms, plgm) + job, err := newSetSyncJob(ctx, rw, rw, kms, plgm) require.NoError(err) - require.NotNil(repo) + require.NotNil(job) in := tt.in() - got, err := repo.UpsertHosts(ctx, in.catalog, in.sets, in.phs, tt.opts...) + got, err := job.upsertHosts(ctx, in.catalog, in.sets, in.phs, tt.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -218,6 +220,10 @@ func TestRepository_UpsertHosts(t *testing.T) { ), ) + repo, err := NewRepository(rw, rw, kms, sched, plgm) + require.NoError(err) + require.NotNil(repo) + // Check again, but via performing an explicit list got, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId()) require.NoError(err) diff --git a/internal/host/plugin/repository_test.go b/internal/host/plugin/repository_test.go index dc4e32b09..594882b00 100644 --- a/internal/host/plugin/repository_test.go +++ b/internal/host/plugin/repository_test.go @@ -3,6 +3,7 @@ package plugin import ( "testing" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,15 +19,17 @@ func TestRepository_New(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kmsCache := kms.TestKms(t, conn, wrapper) + sched := scheduler.TestScheduler(t, conn, wrapper) plgs := map[string]plgpb.HostPluginServiceClient{} type args struct { - r db.Reader - w db.Writer - kms *kms.Kms - plugins map[string]plgpb.HostPluginServiceClient - opts []host.Option + r db.Reader + w db.Writer + kms *kms.Kms + scheduler *scheduler.Scheduler + plugins map[string]plgpb.HostPluginServiceClient + opts []host.Option } tests := []struct { @@ -38,15 +41,17 @@ func TestRepository_New(t *testing.T) { { name: "valid", args: args{ - r: rw, - w: rw, - kms: kmsCache, - plugins: plgs, + r: rw, + w: rw, + kms: kmsCache, + scheduler: sched, + plugins: plgs, }, want: &Repository{ reader: rw, writer: rw, kms: kmsCache, + scheduler: sched, plugins: plgs, defaultLimit: db.DefaultLimit, }, @@ -54,16 +59,18 @@ func TestRepository_New(t *testing.T) { { name: "valid-with-limit", args: args{ - r: rw, - w: rw, - kms: kmsCache, - plugins: plgs, - opts: []host.Option{host.WithLimit(5)}, + r: rw, + w: rw, + kms: kmsCache, + scheduler: sched, + plugins: plgs, + opts: []host.Option{host.WithLimit(5)}, }, want: &Repository{ reader: rw, writer: rw, kms: kmsCache, + scheduler: sched, plugins: plgs, defaultLimit: 5, }, @@ -71,10 +78,11 @@ func TestRepository_New(t *testing.T) { { name: "nil-reader", args: args{ - r: nil, - w: rw, - kms: kmsCache, - plugins: plgs, + r: nil, + w: rw, + kms: kmsCache, + scheduler: sched, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -82,10 +90,11 @@ func TestRepository_New(t *testing.T) { { name: "nil-writer", args: args{ - r: rw, - w: nil, - kms: kmsCache, - plugins: plgs, + r: rw, + w: nil, + kms: kmsCache, + scheduler: sched, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -93,10 +102,11 @@ func TestRepository_New(t *testing.T) { { name: "nil-kms", args: args{ - r: rw, - w: rw, - kms: nil, - plugins: plgs, + r: rw, + w: rw, + kms: nil, + scheduler: sched, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -104,10 +114,23 @@ func TestRepository_New(t *testing.T) { { name: "nil-plugins", args: args{ - r: rw, - w: rw, - kms: kmsCache, - plugins: nil, + r: rw, + w: rw, + kms: kmsCache, + scheduler: sched, + plugins: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-scheduler", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + scheduler: nil, + plugins: plgs, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -115,10 +138,11 @@ func TestRepository_New(t *testing.T) { { name: "all-nils", args: args{ - r: nil, - w: nil, - kms: nil, - plugins: nil, + r: nil, + w: nil, + kms: nil, + scheduler: nil, + plugins: nil, }, want: nil, wantIsErr: errors.InvalidParameter, @@ -128,7 +152,7 @@ func TestRepository_New(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.plugins, tt.args.opts...) + got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.scheduler, tt.args.plugins, tt.args.opts...) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index b0771bf35..8b61078c5 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -169,26 +169,32 @@ type HostSet struct { // The update_time is set by the database. // @inject_tag: `gorm:"default:current_timestamp"` UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // The last_sync_time is updated every time a host set has been synced. + // @inject_tag: `gorm:"default:null"` + LastSyncTime *timestamp.Timestamp `protobuf:"bytes,4,opt,name=last_sync_time,json=lastSyncTime,proto3" json:"last_sync_time,omitempty" gorm:"default:null"` + // The need_sync indicates that a sync needs to happen. + // @inject_tag: `gorm:"not_null"` + NeedSync bool `protobuf:"varint,5,opt,name=need_sync,json=needSync,proto3" json:"need_sync,omitempty" gorm:"not_null"` // name is optional. If set, it must be unique within // catalog_id. // @inject_tag: `gorm:"default:null"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` // description is optional. // @inject_tag: `gorm:"default:null"` - Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` // catalog_id is the public_id of the owning // plugin_host_catalog and must be set. // @inject_tag: `gorm:"not_null"` - CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` + CatalogId string `protobuf:"bytes,8,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"` // version allows optimistic locking of the resource // @inject_tag: `gorm:"default:null"` - Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + Version uint32 `protobuf:"varint,9,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` // attributes is a byte field containing marshaled JSON data // @inject_tag: `gorm:"not_null"` - Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"` + Attributes []byte `protobuf:"bytes,10,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"` // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` - PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` + PreferredEndpoints []string `protobuf:"bytes,11,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` } func (x *HostSet) Reset() { @@ -244,6 +250,20 @@ func (x *HostSet) GetUpdateTime() *timestamp.Timestamp { return nil } +func (x *HostSet) GetLastSyncTime() *timestamp.Timestamp { + if x != nil { + return x.LastSyncTime + } + return nil +} + +func (x *HostSet) GetNeedSync() bool { + if x != nil { + return x.NeedSync + } + return false +} + func (x *HostSet) GetName() string { if x != nil { return x.Name @@ -639,7 +659,7 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xa1, 0x04, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -651,76 +671,83 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, - 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, - 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, - 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, - 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x04, 0x48, 0x6f, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, - 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73, - 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, - 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, - 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, - 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x50, 0x0a, 0x0e, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x24, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, + 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, + 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, + 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, + 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, + 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -749,15 +776,16 @@ var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{ 5, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp 5, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp 5, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 5, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 5, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 5, // 6: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 5, // 7: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 5, // 4: controller.storage.host.plugin.store.v1.HostSet.last_sync_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 6: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 7: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 8: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_controller_storage_host_plugin_store_v1_host_proto_init() } diff --git a/internal/host/plugin/testing.go b/internal/host/plugin/testing.go index c3c7e20c5..6f6b2a62f 100644 --- a/internal/host/plugin/testing.go +++ b/internal/host/plugin/testing.go @@ -6,12 +6,15 @@ import ( "fmt" "io" "net" + "sort" "testing" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/stretchr/testify/assert" @@ -42,7 +45,7 @@ func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Opt require.NoError(t, err) assert.NotNil(t, cat) - plg := host.NewPlugin() + plg := hostplugin.NewPlugin() plg.PublicId = pluginId require.NoError(t, w.LookupByPublicId(ctx, plg)) @@ -58,20 +61,20 @@ func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Opt // TestSet creates a plugin host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) *HostSet { +func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, sched *scheduler.Scheduler, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) *HostSet { t.Helper() require := require.New(t) ctx := context.Background() rw := db.New(conn) - repo, err := NewRepository(rw, rw, kmsCache, plgm) + repo, err := NewRepository(rw, rw, kmsCache, sched, plgm) require.NoError(err) set, err := NewHostSet(ctx, hc.PublicId, opt...) require.NoError(err) require.NotNil(set) - plg := host.NewPlugin() + plg := hostplugin.NewPlugin() plg.PublicId = hc.GetPluginId() require.NoError(rw.LookupByPublicId(ctx, plg)) @@ -85,6 +88,66 @@ func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm return set } +// TestSetMembers adds hosts to the specified setId in the provided DB. +// The set and hosts must have been created previously and belong to the +// same catalog. The test will fail if any errors are encountered. +func TestSetMembers(t *testing.T, conn *db.DB, setId string, hosts []*Host) []*HostSetMember { + t.Helper() + assert := assert.New(t) + + var members []*HostSetMember + for _, host := range hosts { + member, err := NewHostSetMember(context.Background(), setId, host.PublicId) + assert.NoError(err) + assert.NotNil(member) + + w := db.New(conn) + err2 := w.Create(context.Background(), member) + assert.NoError(err2) + members = append(members, member) + } + return members +} + +// TestHost creates a plugin host in the provided DB in the catalog with the +// provided catalog id. The catalog must have been created previously. +// The test will fail if any errors are encountered. +func TestHost(t *testing.T, conn *db.DB, catId, externId string, opt ...Option) *Host { + t.Helper() + w := db.New(conn) + ctx := context.Background() + host1 := NewHost(ctx, catId, externId, opt...) + var err error + host1.PublicId, err = newHostId(ctx, catId, externId) + require.NoError(t, err) + require.NoError(t, w.Create(ctx, host1)) + + var ipAddresses []interface{} + if len(host1.GetIpAddresses()) > 0 { + sort.Strings(host1.IpAddresses) + ipAddresses = make([]interface{}, 0, len(host1.GetIpAddresses())) + for _, a := range host1.GetIpAddresses() { + obj, err := host.NewIpAddress(ctx, host1.PublicId, a) + require.NoError(t, err) + ipAddresses = append(ipAddresses, obj) + } + require.NoError(t, w.CreateItems(ctx, ipAddresses)) + } + + var dnsNames []interface{} + if len(host1.GetDnsNames()) > 0 { + sort.Strings(host1.DnsNames) + dnsNames = make([]interface{}, 0, len(host1.GetDnsNames())) + for _, n := range host1.GetDnsNames() { + obj, err := host.NewDnsName(ctx, host1.PublicId, n) + require.NoError(t, err) + dnsNames = append(dnsNames, obj) + } + require.NoError(t, w.CreateItems(ctx, dnsNames)) + } + return host1 +} + func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, count int) ([]*plgpb.ListHostsResponseHost, []*Host) { t.Helper() require := require.New(t) @@ -133,6 +196,17 @@ func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, coun return retRH, retH } +// TestRunSetSync runs the set sync job a single time. +func TestRunSetSync(t *testing.T, conn *db.DB, kmsCache *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient) { + t.Helper() + rw := db.New(conn) + ctx := context.Background() + + j, err := newSetSyncJob(ctx, rw, rw, kmsCache, plgm) + require.NoError(t, err) + require.NoError(t, j.Run(ctx)) +} + func testGetDnsName(t *testing.T) string { dnsName, err := base62.Random(10) require.NoError(t, err) diff --git a/internal/host/plugin/testing_test.go b/internal/host/plugin/testing_test.go index f74526c63..3b75a30b8 100644 --- a/internal/host/plugin/testing_test.go +++ b/internal/host/plugin/testing_test.go @@ -1,12 +1,14 @@ package plugin import ( + "context" "testing" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,6 +37,7 @@ func Test_TestSet(t *testing.T) { assert, require := assert.New(t), require.New(t) conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) require.NotNil(prj) @@ -45,9 +48,95 @@ func Test_TestSet(t *testing.T) { assert.NotEmpty(plg.GetPublicId()) c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) - set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceClient{plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{})}, WithName("foo"), WithDescription("bar")) + set := TestSet(t, conn, kmsCache, sched, c, map[string]plgpb.HostPluginServiceClient{plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{})}, WithName("foo"), WithDescription("bar")) assert.NotEmpty(set.GetPublicId()) db.AssertPublicId(t, HostSetPrefix, set.GetPublicId()) assert.Equal("foo", set.GetName()) assert.Equal("bar", set.GetDescription()) } + +func Test_TestHosts(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + plg := host.TestPlugin(t, conn, "test") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) + + require.NotNil(org) + assert.NotEmpty(org.GetPublicId()) + + c := TestCatalog(t, conn, org.GetPublicId(), plg.GetPublicId()) + + h := TestHost(t, conn, c.GetPublicId(), plg.GetPublicId()) + assert.NotEmpty(h.GetPublicId()) +} + +func Test_TestSetMembers(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) + kmsCache := kms.TestKms(t, conn, wrapper) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + plg := host.TestPlugin(t, conn, "test") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) + + c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) + s := TestSet(t, conn, kmsCache, sched, c, map[string]plgpb.HostPluginServiceClient{plg.GetPublicId(): NewWrappingPluginClient(&TestPluginServer{})}) + + h := TestHost(t, conn, c.GetPublicId(), plg.GetPublicId()) + members := TestSetMembers(t, conn, s.PublicId, []*Host{h}) + assert.Len(members, 1) + assert.Equal(h.GetPublicId(), members[0].GetHostId()) + assert.Equal(s.GetPublicId(), members[0].GetSetId()) +} + +func Test_TestRunSetSync(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) + kmsCache := kms.TestKms(t, conn, wrapper) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + plg := host.TestPlugin(t, conn, "test") + require.NotNil(plg) + assert.NotEmpty(plg.GetPublicId()) + pluginServer := &TestPluginServer{} + plgm := map[string]plgpb.HostPluginServiceClient{plg.GetPublicId(): NewWrappingPluginClient(pluginServer)} + + c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId()) + s1 := TestSet(t, conn, kmsCache, sched, c, plgm) + s2 := TestSet(t, conn, kmsCache, sched, c, plgm) + + pluginServer.ListHostsFn = func(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) { + var setIds []string + for _, s := range req.GetSets() { + setIds = append(setIds, s.GetId()) + } + resp := &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{ + { + ExternalId: "test", + SetIds: setIds, + IpAddresses: []string{"10.0.0.1"}, + }, + }} + return resp, nil + } + + TestRunSetSync(t, conn, kmsCache, plgm) + rw := db.New(conn) + var ha []*hostAgg + require.NoError(rw.SearchWhere(context.Background(), &ha, "true", nil)) + require.Len(ha, 1) + assert.ElementsMatch(ha[0].toHost().SetIds, []string{s1.GetPublicId(), s2.GetPublicId()}) +} diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go index e58700bd0..47151564f 100644 --- a/internal/host/preferred_endpoint_test.go +++ b/internal/host/preferred_endpoint_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,13 +22,14 @@ func TestPreferredEndpoint_Create(t *testing.T) { ctx := context.TODO() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) rw := db.New(conn) kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceClient{ + set := plugin.TestSet(t, conn, kmsCache, sched, catalog, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -151,13 +153,14 @@ func TestPreferredEndpoint_Delete(t *testing.T) { ctx := context.TODO() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, conn, wrapper) rw := db.New(conn) kmsCache := kms.TestKms(t, conn, wrapper) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) plg := hostplg.TestPlugin(t, conn, "create") catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceClient{ + set := plugin.TestSet(t, conn, kmsCache, sched, catalog, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), }) diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 6b38203b7..645cc0c04 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -60,31 +60,39 @@ message HostSet { // @inject_tag: `gorm:"default:current_timestamp"` timestamp.v1.Timestamp update_time = 3; + // The last_sync_time is updated every time a host set has been synced. + // @inject_tag: `gorm:"default:null"` + timestamp.v1.Timestamp last_sync_time = 4; + + // The need_sync indicates that a sync needs to happen. + // @inject_tag: `gorm:"not_null"` + bool need_sync = 5; + // name is optional. If set, it must be unique within // catalog_id. // @inject_tag: `gorm:"default:null"` - string name = 4 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; + string name = 6 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; // description is optional. // @inject_tag: `gorm:"default:null"` - string description = 5 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}]; + string description = 7 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}]; // catalog_id is the public_id of the owning // plugin_host_catalog and must be set. // @inject_tag: `gorm:"not_null"` - string catalog_id = 6; + string catalog_id = 8; // version allows optimistic locking of the resource // @inject_tag: `gorm:"default:null"` - uint32 version = 7; + uint32 version = 9; // attributes is a byte field containing marshaled JSON data // @inject_tag: `gorm:"not_null"` - bytes attributes = 8; + bytes attributes = 10; // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` - repeated string preferred_endpoints = 9; + repeated string preferred_endpoints = 11; } message HostCatalogSecret { diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 14e97c180..76ae32e0d 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -183,7 +183,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { return static.NewRepository(dbase, dbase, c.kms) } c.PluginHostRepoFn = func() (*pluginhost.Repository, error) { - return pluginhost.NewRepository(dbase, dbase, c.kms, c.conf.HostPlugins) + return pluginhost.NewRepository(dbase, dbase, c.kms, c.scheduler, c.conf.HostPlugins) } c.HostPluginRepoFn = func() (*host.Repository, error) { return host.NewRepository(dbase, dbase, c.kms) @@ -262,6 +262,9 @@ func (c *Controller) registerJobs() error { if err := vault.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms); err != nil { return err } + if err := pluginhost.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.HostPlugins); err != nil { + return err + } if err := c.registerSessionCleanupJob(); err != nil { return err diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 3d2645035..8574272ff 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" @@ -67,6 +68,7 @@ func TestGet_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) rw := db.New(conn) @@ -74,7 +76,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -156,6 +158,7 @@ func TestGet_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) rw := db.New(conn) @@ -163,7 +166,7 @@ func TestGet_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -258,11 +261,12 @@ func TestList(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { return iam.TestRepo(t, conn, wrapper), nil } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -460,6 +464,7 @@ func TestDelete_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -467,7 +472,7 @@ func TestDelete_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -529,6 +534,7 @@ func TestDelete_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -536,7 +542,7 @@ func TestDelete_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -600,6 +606,7 @@ func TestDelete_twice(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -607,7 +614,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -635,6 +642,7 @@ func TestCreate_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -642,7 +650,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) @@ -786,6 +794,7 @@ func TestCreate_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -802,7 +811,7 @@ func TestCreate_Plugin(t *testing.T) { name := "test" plg := host.TestPlugin(t, conn, name) pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{ + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{ OnCreateCatalogFn: func(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { return nil, nil @@ -962,6 +971,7 @@ func TestUpdate(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) @@ -969,7 +979,7 @@ func TestUpdate(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepo := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } pluginRepo := func() (*host.Repository, error) { return host.NewRepository(rw, rw, kms) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index d7c630028..b7c36ecdc 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" @@ -44,6 +45,7 @@ func TestGet_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -57,7 +59,7 @@ func TestGet_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -137,6 +139,7 @@ func TestGet_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -157,11 +160,11 @@ func TestGet_Plugin(t *testing.T) { plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, plgm) + return plugin.NewRepository(rw, rw, kms, sche, plgm) } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints)) + hs := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints)) toMerge := &pbs.GetHostSetRequest{} @@ -240,6 +243,7 @@ func TestList_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -253,7 +257,7 @@ func TestList_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2) hc, hcNoHosts := hcs[0], hcs[1] @@ -338,6 +342,7 @@ func TestList_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -356,7 +361,7 @@ func TestList_Plugin(t *testing.T) { plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, plgm) + return plugin.NewRepository(rw, rw, kms, sche, plgm) } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) @@ -364,7 +369,7 @@ func TestList_Plugin(t *testing.T) { var wantHs []*pb.HostSet for i := 0; i < 10; i++ { - h := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) + h := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) wantHs = append(wantHs, &pb.HostSet{ Id: h.GetPublicId(), HostCatalogId: h.GetCatalogId(), @@ -450,6 +455,7 @@ func TestDelete_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -463,7 +469,7 @@ func TestDelete_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -520,6 +526,7 @@ func TestDelete_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -533,7 +540,7 @@ func TestDelete_Plugin(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } name := "test" plg := hostplugin.TestPlugin(t, conn, name) @@ -542,7 +549,7 @@ func TestDelete_Plugin(t *testing.T) { } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - h := plugin.TestSet(t, conn, kms, hc, plgm) + h := plugin.TestSet(t, conn, kms, sche, hc, plgm) s, err := host_sets.NewService(repoFn, pluginRepoFn) require.NoError(t, err, "Couldn't create a new host set service.") @@ -597,6 +604,7 @@ func TestDelete_twice(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -610,7 +618,7 @@ func TestDelete_twice(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -633,6 +641,7 @@ func TestCreate_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -646,7 +655,7 @@ func TestCreate_Static(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -776,6 +785,7 @@ func TestCreate_Plugin(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -791,7 +801,7 @@ func TestCreate_Plugin(t *testing.T) { name := "test" plg := hostplugin.TestPlugin(t, conn, name) plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{ + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{ OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { return nil, nil @@ -1028,6 +1038,7 @@ func TestUpdate(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1070,7 +1081,7 @@ func TestUpdate(t *testing.T) { } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } tested, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Failed to create a new host set service.") @@ -1407,6 +1418,7 @@ func TestAddHostSetHosts(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1420,7 +1432,7 @@ func TestAddHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1527,6 +1539,7 @@ func TestSetHostSetHosts(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1540,7 +1553,7 @@ func TestSetHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") @@ -1643,6 +1656,7 @@ func TestRemoveHostSetHosts(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1656,7 +1670,7 @@ func TestRemoveHostSetHosts(t *testing.T) { return static.NewRepository(rw, rw, kms) } plgRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } s, err := host_sets.NewService(repoFn, plgRepoFn) require.NoError(t, err, "Error when getting new host set service.") diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 2570cddae..a7d51b883 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -90,7 +90,7 @@ func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapp return static.NewRepository(rw, rw, kms) } pluginHostRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) @@ -974,6 +974,7 @@ func TestAddTargetHostSets(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -990,7 +991,7 @@ func TestAddTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -1108,6 +1109,7 @@ func TestSetTargetHostSets(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1124,7 +1126,7 @@ func TestSetTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -1236,6 +1238,7 @@ func TestRemoveTargetHostSets(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1252,7 +1255,7 @@ func TestRemoveTargetHostSets(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -1376,6 +1379,7 @@ func TestAddTargetHostSources(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1392,7 +1396,7 @@ func TestAddTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -1510,6 +1514,7 @@ func TestSetTargetHostSources(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1526,7 +1531,7 @@ func TestSetTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -1632,6 +1637,7 @@ func TestRemoveTargetHostSources(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) iamRepoFn := func() (*iam.Repository, error) { @@ -1648,7 +1654,7 @@ func TestRemoveTargetHostSources(t *testing.T) { plg := host.TestPlugin(t, conn, "test") pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceClient{ + pluginHs := plugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{ plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), }) @@ -2563,6 +2569,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) { } func TestAuthorizeSession(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -2619,12 +2626,12 @@ func TestAuthorizeSession(t *testing.T) { }), } pluginHostRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, plgm) + return plugin.NewRepository(rw, rw, kms, sche, plgm) } org, proj := iam.TestScopes(t, iamRepo) at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) - ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), + ctx = auth.NewVerifierContext(requests.NewRequestContext(ctx), iamRepoFn, atRepoFn, serversRepoFn, @@ -2648,7 +2655,10 @@ func TestAuthorizeSession(t *testing.T) { _ = static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{h}) phc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - phs := plugin.TestSet(t, conn, kms, phc, plgm, plugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"})) + phs := plugin.TestSet(t, conn, kms, sche, phc, plgm, plugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"})) + + // Sync the boundary db from the plugins + plugin.TestRunSetSync(t, conn, kms, plgm) v := vault.NewTestVaultServer(t) v.MountPKI(t) @@ -2824,7 +2834,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return static.NewRepository(rw, rw, kms) } pluginHostRepoFn := func() (*plugin.Repository, error) { - return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceClient{}) + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } credentialRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) From 808b986834e2c5f14a0f32f0d9029ac6581e1645 Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 2 Nov 2021 14:06:20 -0700 Subject: [PATCH 44/71] Read and List Plugin based Hosts (#1655) * Add ability to read and list hosts. Tests to follow. * Add tests for reading and listing as well as validation checks to not modify hosts. * Add IpAddress and DnsNames to the api resource. Populate the plugin info. * Adding go sdk tests for reading and listing hosts and ensuring update and deleting is disallowed. --- api/hosts/host.gen.go | 2 + globals/fields.go | 2 + internal/cmd/config/config.go | 6 + internal/gen/controller.swagger.json | 16 + internal/host/plugin/host_set_member_test.go | 8 +- internal/host/plugin/ids.go | 2 +- internal/host/plugin/repository_host.go | 33 +- internal/host/plugin/repository_host_set.go | 2 +- internal/host/plugin/repository_host_test.go | 22 +- .../api/resources/hosts/v1/host.proto | 6 + internal/servers/controller/controller.go | 6 +- internal/servers/controller/handler.go | 2 +- .../controller/handlers/hosts/host_service.go | 266 ++++++++++---- .../handlers/hosts/host_service_test.go | 328 +++++++++++++++++- internal/servers/controller/testing.go | 5 + .../api/hostcatalogs/host_catalog_test.go | 46 +++ internal/tests/api/hosts/host_test.go | 49 +++ internal/tests/api/hostsets/host_set_test.go | 51 +++ .../controller/api/resources/hosts/host.pb.go | 58 +++- 19 files changed, 803 insertions(+), 107 deletions(-) diff --git a/api/hosts/host.gen.go b/api/hosts/host.gen.go index 24dcc2178..718ffd667 100644 --- a/api/hosts/host.gen.go +++ b/api/hosts/host.gen.go @@ -26,6 +26,8 @@ type Host struct { Type string `json:"type,omitempty"` HostSetIds []string `json:"host_set_ids,omitempty"` Attributes map[string]interface{} `json:"attributes,omitempty"` + IpAddresses []string `json:"ip_addresses,omitempty"` + DnsNames []string `json:"dns_names,omitempty"` AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response diff --git a/globals/fields.go b/globals/fields.go index fe6738718..3ee466d03 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -62,4 +62,6 @@ const ( PluginIdField = "plugin_id" PluginField = "plugin" PluginNameField = "plugin_name" + IpAddressesField = "ip_addresses" + DnsNamesField = "dns_names" ) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index bd47130d7..824ce0523 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -131,6 +131,12 @@ type Controller struct { // // TODO: This field is currently internal. StatusGracePeriodDuration time.Duration `hcl:"-"` + + // SchedulerRunJobInterval is the time interval between waking up the + // scheduler to run pending jobs. + // + // TODO: This field is currently internal. + SchedulerRunJobInterval time.Duration `hcl:"-"` } func (c *Controller) InitNameIfEmpty() (string, error) { diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 8b5f11ef0..81d63567f 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -4213,6 +4213,22 @@ "type": "object", "description": "The attributes that are applicable to the specific Host type." }, + "ip_addresses": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Output only. The list of ip addresses associated with this host.", + "readOnly": true + }, + "dns_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Output only. The list of dns addresses associated with this host.", + "readOnly": true + }, "authorized_actions": { "type": "array", "items": { diff --git a/internal/host/plugin/host_set_member_test.go b/internal/host/plugin/host_set_member_test.go index 2002d0360..056552db8 100644 --- a/internal/host/plugin/host_set_member_test.go +++ b/internal/host/plugin/host_set_member_test.go @@ -157,7 +157,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { require.Len(t, hosts, 2) // Base case the count by catalog ID - hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) assert.Len(t, hosts, 4) @@ -174,7 +174,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost1.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) - hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) require.Len(t, hosts, 3) @@ -189,7 +189,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost2.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) - hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) require.Len(t, hosts, 2) @@ -206,7 +206,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) { assert.Equal(t, 2, count) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost3.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost4.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) - hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) + hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId) require.NoError(t, err) require.Len(t, hosts, 0) } diff --git a/internal/host/plugin/ids.go b/internal/host/plugin/ids.go index 0516abead..ab8e77e4e 100644 --- a/internal/host/plugin/ids.go +++ b/internal/host/plugin/ids.go @@ -10,7 +10,7 @@ import ( ) func init() { - if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix); err != nil { + if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix, HostPrefix); err != nil { panic(err) } } diff --git a/internal/host/plugin/repository_host.go b/internal/host/plugin/repository_host.go index 44649f915..5a01cdf10 100644 --- a/internal/host/plugin/repository_host.go +++ b/internal/host/plugin/repository_host.go @@ -6,33 +6,38 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" ) // LookupHost will look up a host in the repository. If the host is not // found, it will return nil, nil. All options are ignored. -func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, error) { +func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, *hostplugin.Plugin, error) { const op = "plugin.(Repository).LookupHost" if publicId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") } ha := &hostAgg{ PublicId: publicId, } if err := r.reader.LookupByPublicId(ctx, ha); err != nil { if errors.IsNotFoundError(err) { - return nil, nil + return nil, nil, nil } - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) } - return ha.toHost(), nil + plg, err := r.getPlugin(ctx, ha.PluginId) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + return ha.toHost(), plg, nil } // ListHostsByCatalogId returns a slice of Hosts for the catalogId. // WithLimit is the only option supported. -func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) { +func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, opt ...Option) ([]*Host, *hostplugin.Plugin, error) { const op = "plugin.(Repository).ListHostsByCatalogId" if catalogId == "" { - return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id") } opts := getOpts(opt...) limit := r.defaultLimit @@ -45,9 +50,15 @@ func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, switch { case err != nil: - return nil, errors.Wrap(ctx, err, op) - case hostAggs == nil: - return nil, nil + return nil, nil, errors.Wrap(ctx, err, op) + case len(hostAggs) == 0: + return nil, nil, nil + } + + pluginId := hostAggs[0].PluginId + plg, err := r.getPlugin(ctx, pluginId) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) } hosts := make([]*Host, 0, len(hostAggs)) @@ -55,7 +66,7 @@ func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, hosts = append(hosts, ha.toHost()) } - return hosts, nil + return hosts, plg, nil } // ListHostsBySetId returns a slice of Hosts for the given set IDs. diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 3c14e0178..6e3344244 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -143,7 +143,7 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } // The set now exists in the plugin, sync it immediately. - r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0) + _ = r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0) plg, err := r.getPlugin(ctx, c.GetPluginId()) if err != nil { diff --git a/internal/host/plugin/repository_host_test.go b/internal/host/plugin/repository_host_test.go index 63075a98c..002d2f660 100644 --- a/internal/host/plugin/repository_host_test.go +++ b/internal/host/plugin/repository_host_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + hostplgstore "github.com/hashicorp/boundary/internal/plugin/host/store" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" @@ -225,7 +226,8 @@ func TestJob_UpsertHosts(t *testing.T) { require.NotNil(repo) // Check again, but via performing an explicit list - got, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId()) + var gotPlg *hostplg.Plugin + got, gotPlg, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId()) require.NoError(err) require.NotNil(got) assert.Len(got, len(in.phs)) @@ -240,6 +242,14 @@ func TestJob_UpsertHosts(t *testing.T) { }), ), ) + assert.Empty( + cmp.Diff( + plg, + gotPlg, + cmpopts.IgnoreUnexported(hostplg.Plugin{}, hostplgstore.Plugin{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + ), + ) // Now individually call read on each host, cache the matching set // IDs, and then check membership @@ -248,7 +258,7 @@ func TestJob_UpsertHosts(t *testing.T) { for _, setId := range exp.SetIds { setIdMap[setId] = append(setIdMap[setId], exp.GetPublicId()) } - got, err := repo.LookupHost(ctx, exp.GetPublicId()) + got, gotPlg, err := repo.LookupHost(ctx, exp.GetPublicId()) require.NoError(err) require.NotNil(got) assert.NotEmpty(got.SetIds) @@ -260,6 +270,14 @@ func TestJob_UpsertHosts(t *testing.T) { cmpopts.IgnoreTypes(×tamp.Timestamp{}), ), ) + assert.Empty( + cmp.Diff( + plg, + gotPlg, + cmpopts.IgnoreUnexported(hostplg.Plugin{}, hostplgstore.Plugin{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + ), + ) } for setId, expHostIds := range setIdMap { got, err = repo.ListHostsBySetIds(ctx, []string{setId}) diff --git a/internal/proto/local/controller/api/resources/hosts/v1/host.proto b/internal/proto/local/controller/api/resources/hosts/v1/host.proto index 1a7794329..48b37ee90 100644 --- a/internal/proto/local/controller/api/resources/hosts/v1/host.proto +++ b/internal/proto/local/controller/api/resources/hosts/v1/host.proto @@ -50,6 +50,12 @@ message Host { // The attributes that are applicable to the specific Host type. google.protobuf.Struct attributes = 110 [(custom_options.v1.generate_sdk_option) = true]; + // Output only. The list of ip addresses associated with this host. + repeated string ip_addresses = 120; + + // Output only. The list of dns addresses associated with this host. + repeated string dns_names = 130; + // Output only. The available actions on this resource for this user. repeated string authorized_actions = 300 [json_name="authorized_actions"]; } diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 76ae32e0d..164259274 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -172,7 +172,11 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } // TODO: the RunJobsLimit is temporary until a better fix gets in. This // currently caps the scheduler at running 10 jobs per interval. - c.scheduler, err = scheduler.New(c.conf.RawConfig.Controller.Name, jobRepoFn, scheduler.WithRunJobsLimit(10)) + schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(10)} + if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 { + schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval)) + } + c.scheduler, err = scheduler.New(c.conf.RawConfig.Controller.Name, jobRepoFn, schedulerOpts...) if err != nil { return nil, fmt.Errorf("error creating new scheduler: %w", err) } diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 22ea5eddf..8ab4c3b19 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -103,7 +103,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er } } if _, ok := currentServices[services.HostService_ServiceDesc.ServiceName]; !ok { - hs, err := hosts.NewService(c.StaticHostRepoFn) + hs, err := hosts.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn) if err != nil { return nil, fmt.Errorf("failed to create host handler service: %w", err) } diff --git a/internal/servers/controller/handlers/hosts/host_service.go b/internal/servers/controller/handlers/hosts/host_service.go index b5d1ac62a..e62cfffc7 100644 --- a/internal/servers/controller/handlers/hosts/host_service.go +++ b/internal/servers/controller/handlers/hosts/host_service.go @@ -10,9 +10,11 @@ import ( "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/perms" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/common" @@ -21,6 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "google.golang.org/grpc/codes" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -56,18 +59,22 @@ type Service struct { pbs.UnimplementedHostServiceServer staticRepoFn common.StaticRepoFactory + pluginRepoFn common.PluginHostRepoFactory } var _ pbs.HostServiceServer = Service{} // NewService returns a host Service which handles host related requests to boundary and uses the provided // repositories for storage and retrieval. -func NewService(repoFn common.StaticRepoFactory) (Service, error) { +func NewService(repoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory) (Service, error) { const op = "hosts.NewService" if repoFn == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static repository") } - return Service{staticRepoFn: repoFn}, nil + if pluginRepoFn == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing plugin host repository") + } + return Service{staticRepoFn: repoFn, pluginRepoFn: pluginRepoFn}, nil } func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs.ListHostsResponse, error) { @@ -78,7 +85,7 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs if authResults.Error != nil { return nil, authResults.Error } - hl, err := s.listFromRepo(ctx, req.GetHostCatalogId()) + hl, plg, err := s.listFromRepo(ctx, req.GetHostCatalogId()) if err != nil { return nil, err } @@ -107,6 +114,9 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId) outputOpts := make([]handlers.Option, 0, 3) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } @@ -114,7 +124,12 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) } - item, err := toProto(ctx, item, nil, outputOpts...) + var hostSetIds []string + switch h := item.(type) { + case *plugin.Host: + hostSetIds = h.SetIds + } + item, err := toProto(ctx, item, hostSetIds, outputOpts...) if err != nil { return nil, err } @@ -137,7 +152,7 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get if authResults.Error != nil { return nil, authResults.Error } - h, err := s.getFromRepo(ctx, req.GetId()) + h, plg, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err } @@ -149,6 +164,9 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get outputOpts := make([]handlers.Option, 0, 3) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } @@ -156,7 +174,12 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings())) } - item, err := toProto(ctx, h, nil, outputOpts...) + var hostSetIds []string + switch h := h.(type) { + case *plugin.Host: + hostSetIds = h.SetIds + } + item, err := toProto(ctx, h, hostSetIds, outputOpts...) if err != nil { return nil, err } @@ -259,19 +282,38 @@ func (s Service) DeleteHost(ctx context.Context, req *pbs.DeleteHostRequest) (*p return nil, nil } -func (s Service) getFromRepo(ctx context.Context, id string) (*static.Host, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, err +func (s Service) getFromRepo(ctx context.Context, id string) (host.Host, *plugins.PluginInfo, error) { + var h host.Host + var plg *plugins.PluginInfo + switch host.SubtypeFromId(id) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, nil, err + } + h, err = repo.LookupHost(ctx, id) + if err != nil { + return nil, nil, err + } + if h == nil { + return nil, nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id) + } + case plugin.Subtype: + repo, err := s.pluginRepoFn() + if err != nil { + return nil, nil, err + } + ph, hPlg, err := repo.LookupHost(ctx, id) + if err != nil { + return nil, nil, err + } + if ph == nil { + return nil, nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id) + } + h = ph + plg = toPluginInfo(hPlg) } - h, err := repo.LookupHost(ctx, id) - if err != nil { - return nil, err - } - if h == nil { - return nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id) - } - return h, nil + return h, plg, nil } func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.Host) (*static.Host, error) { @@ -361,21 +403,47 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, return rows > 0, nil } -func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*static.Host, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, err +func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Host, *plugins.PluginInfo, error) { + var hosts []host.Host + var plg *plugins.PluginInfo + switch host.SubtypeFromId(catalogId) { + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, nil, err + } + hl, err := repo.ListHosts(ctx, catalogId) + if err != nil { + return nil, nil, err + } + for _, h := range hl { + hosts = append(hosts, h) + } + case plugin.Subtype: + repo, err := s.pluginRepoFn() + if err != nil { + return nil, nil, err + } + hl, hlPlg, err := repo.ListHostsByCatalogId(ctx, catalogId) + if err != nil { + return nil, nil, err + } + for _, h := range hl { + hosts = append(hosts, h) + } + plg = toPluginInfo(hlPlg) } - hl, err := repo.ListHosts(ctx, catalogId) - if err != nil { - return nil, err - } - return hl, nil + return hosts, plg, nil } -func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) { +func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) { res := auth.VerifyResults{} - repo, err := s.staticRepoFn() + staticRepo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return nil, res + } + pluginRepo, err := s.pluginRepoFn() if err != nil { res.Error = err return nil, res @@ -387,33 +455,72 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty case action.List, action.Create: parentId = id default: - h, err := repo.LookupHost(ctx, id) + switch host.SubtypeFromId(id) { + case static.Subtype: + h, err := staticRepo.LookupHost(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if h == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + parentId = h.GetCatalogId() + case plugin.Subtype: + h, _, err := pluginRepo.LookupHost(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if h == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + parentId = h.GetCatalogId() + } + opts = append(opts, auth.WithId(id)) + } + + var cat host.Catalog + switch host.SubtypeFromId(id) { + case static.Subtype: + cat, err = staticRepo.LookupCatalog(ctx, parentId) if err != nil { res.Error = err return nil, res } - if h == nil { + if cat == nil { + res.Error = handlers.NotFoundError() + return nil, res + } + case plugin.Subtype: + cat, _, err = pluginRepo.LookupCatalog(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if cat == nil { res.Error = handlers.NotFoundError() return nil, res } - parentId = h.GetCatalogId() - opts = append(opts, auth.WithId(id)) - } - - cat, err := repo.LookupCatalog(ctx, parentId) - if err != nil { - res.Error = err - return nil, res - } - if cat == nil { - res.Error = handlers.NotFoundError() - return nil, res } opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId)) return cat, auth.Verify(ctx, opts...) } -func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, opt ...handlers.Option) (*pb.Host, error) { +func toPluginInfo(plg *hostplugin.Plugin) *plugins.PluginInfo { + if plg == nil { + return nil + } + return &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + } +} + +func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...handlers.Option) (*pb.Host, error) { opts := handlers.GetOpts(opt...) if opts.WithOutputFields == nil { return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building host proto") @@ -428,7 +535,12 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o out.HostCatalogId = in.GetCatalogId() } if outputFields.Has(globals.TypeField) { - out.Type = static.Subtype.String() + switch in.(type) { + case *static.Host: + out.Type = static.Subtype.String() + case *plugin.Host: + out.Type = plugin.Subtype.String() + } } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { out.Description = &wrapperspb.StringValue{Value: in.GetDescription()} @@ -443,7 +555,12 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o out.UpdatedTime = in.GetUpdateTime().GetTimestamp() } if outputFields.Has(globals.VersionField) { - out.Version = in.GetVersion() + switch in.(type) { + case *static.Host: + out.Version = in.GetVersion() + case *plugin.Host: + out.Version = 1 + } } if outputFields.Has(globals.ScopeField) { out.Scope = opts.WithScope @@ -452,16 +569,31 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o out.AuthorizedActions = opts.WithAuthorizedActions } if outputFields.Has(globals.HostSetIdsField) { - for _, hs := range hostSets { - out.HostSetIds = append(out.HostSetIds, hs.GetPublicId()) + for _, hs := range hostSetIds { + out.HostSetIds = append(out.HostSetIds, hs) } } if outputFields.Has(globals.AttributesField) { - st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(in.GetAddress())}) - if err != nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err) + switch h := in.(type) { + case *static.Host: + st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(h.GetAddress())}) + if err != nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err) + } + out.Attributes = st + } + } + if outputFields.Has(globals.PluginField) { + out.Plugin = opts.WithPlugin + } + switch h := in.(type) { + case *plugin.Host: + if outputFields.Has(globals.IpAddressesField) { + out.IpAddresses = h.IpAddresses + } + if outputFields.Has(globals.DnsNamesField) { + out.DnsNames = h.DnsNames } - out.Attributes = st } return &out, nil } @@ -481,7 +613,7 @@ func validateGetRequest(req *pbs.GetHostRequest) error { badFields["id"] = "Improperly formatted identifier used." } return badFields - }, req, static.HostPrefix) + }, req, static.HostPrefix, plugin.HostPrefix) } func validateCreateRequest(req *pbs.CreateHostRequest) error { @@ -493,11 +625,17 @@ func validateCreateRequest(req *pbs.CreateHostRequest) error { switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) { case static.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { - badFields["type"] = "Doesn't match the parent resource's type." + badFields[globals.TypeField] = "Doesn't match the parent resource's type." + } + if len(req.GetItem().GetIpAddresses()) > 0 { + badFields[globals.IpAddressesField] = "This field is not supported for this host type." + } + if len(req.GetItem().GetDnsNames()) > 0 { + badFields[globals.DnsNamesField] = "This field is not supported for this host type." } attrs := &pb.StaticHostAttributes{} if err := handlers.StructToProto(req.GetItem().GetAttributes(), attrs); err != nil { - badFields["attributes"] = "Attribute fields do not match the expected format." + badFields[globals.AttributesField] = "Attribute fields do not match the expected format." } if attrs.GetAddress() == nil || len(attrs.GetAddress().GetValue()) < static.MinHostAddressLength || @@ -513,6 +651,8 @@ func validateCreateRequest(req *pbs.CreateHostRequest) error { default: badFields["attributes.address"] = fmt.Sprintf("Error parsing address: %v.", err) } + case plugin.Subtype: + badFields[globals.HostCatalogIdField] = "Cannot manually create hosts for this type of catalog." } return badFields }) @@ -524,8 +664,7 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error { switch host.SubtypeFromId(req.GetId()) { case static.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { - badFields["type"] = "Cannot modify the resource type." - + badFields[globals.TypeField] = "Cannot modify the resource type." attrs := &pb.StaticHostAttributes{} if err := handlers.StructToProto(req.GetItem().GetAttributes(), attrs); err != nil { badFields["attributes"] = "Attribute fields do not match the expected format." @@ -539,6 +678,8 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error { } } } + case plugin.Subtype: + badFields[globals.IdField] = "Cannot modify this type of host." default: badFields["id"] = "Improperly formatted identifier used." } @@ -547,12 +688,19 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error { } func validateDeleteRequest(req *pbs.DeleteHostRequest) error { - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostPrefix) + return handlers.ValidateDeleteRequest(func() map[string]string { + badFields := map[string]string{} + switch host.SubtypeFromId(req.GetId()) { + case plugin.Subtype: + badFields[globals.IdField] = "Cannot manually delete this type of host." + } + return badFields + }, req, static.HostPrefix) } func validateListRequest(req *pbs.ListHostsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix) { + if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, plugin.HostCatalogPrefix) { badFields["host_catalog_id"] = "The field is incorrectly formatted." } if _, err := handlers.NewFilter(req.GetFilter()); err != nil { diff --git a/internal/servers/controller/handlers/hosts/host_service_test.go b/internal/servers/controller/handlers/hosts/host_service_test.go index b7cfc7a65..61b7d58fd 100644 --- a/internal/servers/controller/handlers/hosts/host_service_test.go +++ b/internal/servers/controller/handlers/hosts/host_service_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "sort" "strings" "testing" @@ -11,15 +12,20 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" + "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/genproto/protobuf/field_mask" @@ -28,11 +34,12 @@ import ( "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" ) var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} -func TestGet(t *testing.T) { +func TestGet_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -46,6 +53,10 @@ func TestGet(t *testing.T) { org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -98,8 +109,8 @@ func TestGet(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := hosts.NewService(repoFn) - require.NoError(err, "Couldn't create a new host set service.") + s, err := hosts.NewService(repoFn, pluginRepoFn) + require.NoError(err, "Couldn't create a new host service.") got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) if tc.err != nil { @@ -115,7 +126,106 @@ func TestGet(t *testing.T) { } } -func TestList(t *testing.T) { +func TestGet_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + org, proj := iam.TestScopes(t, iamRepo) + + plg := hostplugin.TestPlugin(t, conn, "test") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + } + + rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, plgm) + } + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + h := plugin.TestHost(t, conn, hc.GetPublicId(), "test") + hs := plugin.TestSet(t, conn, kms, sche, hc, plgm) + plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h}) + + pHost := &pb.Host{ + HostCatalogId: hc.GetPublicId(), + Id: h.GetPublicId(), + CreatedTime: h.CreateTime.GetTimestamp(), + UpdatedTime: h.UpdateTime.GetTimestamp(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Type: plugin.Subtype.String(), + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, + HostSetIds: []string{hs.GetPublicId()}, + AuthorizedActions: testAuthorizedActions, + } + + cases := []struct { + name string + req *pbs.GetHostRequest + res *pbs.GetHostResponse + err error + }{ + { + name: "Get an Existing Host", + req: &pbs.GetHostRequest{Id: h.GetPublicId()}, + res: &pbs.GetHostResponse{Item: pHost}, + }, + { + name: "non existing", + req: &pbs.GetHostRequest{Id: plugin.HostPrefix + "_DoesntExis"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Wrong id prefix", + req: &pbs.GetHostRequest{Id: "j_1234567890"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "space in id", + req: &pbs.GetHostRequest{Id: plugin.HostPrefix + "_1 23456789"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + s, err := hosts.NewService(repoFn, pluginRepoFn) + require.NoError(err, "Couldn't create a new host service.") + + got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "GetHost(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + return + } + require.NoError(gErr) + + if tc.res != nil { + tc.res.Item.Version = 1 + } + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetHost(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + +func TestList_Static(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) @@ -128,6 +238,10 @@ func TestList(t *testing.T) { org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -185,7 +299,7 @@ func TestList(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := hosts.NewService(repoFn) + s, err := hosts.NewService(repoFn, pluginRepoFn) require.NoError(err, "Couldn't create new host set service.") // Test non-anonymous listing @@ -211,6 +325,124 @@ func TestList(t *testing.T) { } } +func TestList_Plugin(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + org, proj := iam.TestScopes(t, iamRepo) + plg := hostplugin.TestPlugin(t, conn, "test") + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + } + + rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, plgm) + } + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + hcs := plugin.TestCatalogs(t, conn, proj.GetPublicId(), plg.GetPublicId(), 2) + hc, hcNoHosts := hcs[0], hcs[1] + hs := plugin.TestSet(t, conn, kms, sche, hc, plgm) + + var wantHs []*pb.Host + for i := 0; i < 10; i++ { + h := plugin.TestHost(t, conn, hc.GetPublicId(), fmt.Sprintf("host %d", i)) + plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h}) + wantHs = append(wantHs, &pb.Host{ + Id: h.GetPublicId(), + HostCatalogId: h.GetCatalogId(), + Plugin: &plugins.PluginInfo{ + Id: plg.GetPublicId(), + Name: plg.GetName(), + Description: plg.GetDescription(), + }, + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + CreatedTime: h.GetCreateTime().GetTimestamp(), + UpdatedTime: h.GetUpdateTime().GetTimestamp(), + HostSetIds: []string{hs.GetPublicId()}, + Version: 1, + Type: plugin.Subtype.String(), + AuthorizedActions: testAuthorizedActions, + }) + } + sort.Slice(wantHs, func(i, j int) bool { + return wantHs[i].GetId() < wantHs[j].GetId() + }) + + cases := []struct { + name string + req *pbs.ListHostsRequest + res *pbs.ListHostsResponse + err error + }{ + { + name: "List Many Hosts", + req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId()}, + res: &pbs.ListHostsResponse{Items: wantHs}, + }, + { + name: "List No Hosts", + req: &pbs.ListHostsRequest{HostCatalogId: hcNoHosts.GetPublicId()}, + res: &pbs.ListHostsResponse{}, + }, + { + name: "Filter to One Host", + req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantHs[1].GetId())}, + res: &pbs.ListHostsResponse{Items: wantHs[1:2]}, + }, + { + name: "Filter to No Hosts", + req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/id"=="doesnt match"`}, + res: &pbs.ListHostsResponse{}, + }, + { + name: "Filter Bad Format", + req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"//id/"=="bad"`}, + err: handlers.InvalidArgumentErrorf("bad format", nil), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + s, err := hosts.NewService(repoFn, pluginRepoFn) + require.NoError(err, "Couldn't create new host set service.") + + // Test non-anonymous listing + got, gErr := s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "ListHosts(%q) got error %v, wanted %v", tc.req, gErr, tc.err) + return + } + require.NoError(gErr) + + sort.Slice(got.Items, func(i, j int) bool { + return got.Items[i].GetId() < got.Items[j].GetId() + }) + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHosts(%q) got response %q, wanted %q", tc.req, got, tc.res) + + // Test anonymous listing + got, gErr = s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req) + require.NoError(gErr) + assert.Len(got.Items, len(tc.res.Items)) + for _, item := range got.GetItems() { + require.Nil(item.CreatedTime) + require.Nil(item.UpdatedTime) + require.Zero(item.Version) + } + }) + } +} + func TestDelete(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") @@ -225,13 +457,21 @@ func TestDelete(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] - s, err := hosts.NewService(repoFn) + plg := hostplugin.TestPlugin(t, conn, "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + pluginH := plugin.TestHost(t, conn, pluginHc.GetPublicId(), "test") + + s, err := hosts.NewService(repoFn, pluginRepoFn) require.NoError(t, err, "Couldn't create a new host set service.") cases := []struct { @@ -248,6 +488,14 @@ func TestDelete(t *testing.T) { Id: h.GetPublicId(), }, }, + { + name: "Delete a plugin Host", + scopeId: proj.GetPublicId(), + req: &pbs.DeleteHostRequest{ + Id: pluginH.GetPublicId(), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, { name: "Delete bad id Host", scopeId: proj.GetPublicId(), @@ -293,13 +541,17 @@ func TestDelete_twice(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] - s, err := hosts.NewService(repoFn) + s, err := hosts.NewService(repoFn, pluginRepoFn) require.NoError(err, "Couldn't create a new host set service.") req := &pbs.DeleteHostRequest{ Id: h.GetPublicId(), @@ -326,11 +578,18 @@ func TestCreate(t *testing.T) { org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] + plg := hostplugin.TestPlugin(t, conn, "test") + pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime() cases := []struct { @@ -365,6 +624,14 @@ func TestCreate(t *testing.T) { }, }, }, + { + name: "Create a plugin Host", + req: &pbs.CreateHostRequest{Item: &pb.Host{ + HostCatalogId: pluginHc.GetPublicId(), + Type: "plugin", + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, { name: "Create with empty address", req: &pbs.CreateHostRequest{Item: &pb.Host{ @@ -454,7 +721,7 @@ func TestCreate(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := hosts.NewService(repoFn) + s, err := hosts.NewService(repoFn, pluginRepoFn) require.NoError(err, "Failed to create a new host set service.") got, gErr := s.CreateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) @@ -498,6 +765,10 @@ func TestUpdate(t *testing.T) { org, proj := iam.TestScopes(t, iamRepo) rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -525,7 +796,7 @@ func TestUpdate(t *testing.T) { Id: h.GetPublicId(), } - tested, err := hosts.NewService(repoFn) + tested, err := hosts.NewService(repoFn, pluginRepoFn) require.NoError(t, err, "Failed to create a new host set service.") cases := []struct { @@ -887,3 +1158,42 @@ func TestUpdate(t *testing.T) { }) } } + +func TestUpdate_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + _, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + sche := scheduler.TestScheduler(t, conn, wrapper) + pluginRepoFn := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) + } + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + + plg := hostplugin.TestPlugin(t, conn, "test") + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + h := plugin.TestHost(t, conn, hc.GetPublicId(), "test") + + tested, err := hosts.NewService(repoFn, pluginRepoFn) + require.NoError(t, err) + + got, err := tested.UpdateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateHostRequest{ + Id: h.GetPublicId(), + Item: &pb.Host{Name: wrapperspb.String("foo")}, + UpdateMask: &field_mask.FieldMask{Paths: []string{"name"}}, + }) + assert.Nil(t, got) + require.Error(t, err) + assert.True(t, errors.Is(err, handlers.ApiErrorWithCode(codes.InvalidArgument))) +} diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index 11691ad98..1a20eb842 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -385,6 +385,10 @@ type TestControllerOpts struct { // The amount of time to wait before marking connections as closed when a // worker has not reported in StatusGracePeriodDuration time.Duration + + // The amount of time between the scheduler waking up to run it's registered + // jobs. + SchedulerRunJobInterval time.Duration } func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { @@ -478,6 +482,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { t.Fatal(err) } } + opts.Config.Controller.SchedulerRunJobInterval = opts.SchedulerRunJobInterval if err := tc.b.SetupEventing(tc.b.Logger, tc.b.StderrLock, opts.Config.Controller.Name, base.WithEventerConfig(opts.Config.Eventing)); err != nil { t.Fatal(err) diff --git a/internal/tests/api/hostcatalogs/host_catalog_test.go b/internal/tests/api/hostcatalogs/host_catalog_test.go index 12fd5679c..5e60d3eeb 100644 --- a/internal/tests/api/hostcatalogs/host_catalog_test.go +++ b/internal/tests/api/hostcatalogs/host_catalog_test.go @@ -59,6 +59,52 @@ func TestList(t *testing.T) { assert.Equal(filterItem.Id, ul.Items[0].Id) } +func TestList_Plugin(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + catalogClient := hostcatalogs.NewClient(client) + + ul, err := catalogClient.List(tc.Context(), proj.GetPublicId()) + require.NoError(err) + assert.Empty(ul.Items) + + var expected []*hostcatalogs.HostCatalog + for i := 0; i < 10; i++ { + expected = append(expected, &hostcatalogs.HostCatalog{Name: fmt.Sprint(i)}) + } + + cr, err := catalogClient.Create(tc.Context(), "plugin", proj.GetPublicId(), + hostcatalogs.WithName(expected[0].Name), hostcatalogs.WithPluginId("pl_1234567890")) + require.NoError(err) + expected[0] = cr.Item + + ul, err = catalogClient.List(tc.Context(), proj.GetPublicId()) + require.NoError(err) + assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items)) + + for i := 1; i < 10; i++ { + cr, err = catalogClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName(expected[i].Name)) + require.NoError(err) + expected[i] = cr.Item + } + ul, err = catalogClient.List(tc.Context(), proj.GetPublicId()) + require.NoError(err) + assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items)) + + filterItem := ul.Items[3] + ul, err = catalogClient.List(tc.Context(), proj.GetPublicId(), + hostcatalogs.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) + require.NoError(err) + assert.Len(ul.Items, 1) + assert.Equal(filterItem.Id, ul.Items[0].Id) +} + func comparableCatalogSlice(in []*hostcatalogs.HostCatalog) []hostcatalogs.HostCatalog { var filtered []hostcatalogs.HostCatalog for _, i := range in { diff --git a/internal/tests/api/hosts/host_test.go b/internal/tests/api/hosts/host_test.go index 85d8ac0b0..0c4391758 100644 --- a/internal/tests/api/hosts/host_test.go +++ b/internal/tests/api/hosts/host_test.go @@ -4,10 +4,12 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/hostcatalogs" "github.com/hashicorp/boundary/api/hosts" + "github.com/hashicorp/boundary/api/hostsets" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers/controller" @@ -80,6 +82,53 @@ func comparableHostSlice(in []*hosts.Host) []hosts.Host { return filtered } +func TestPluginHosts(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, &controller.TestControllerOpts{SchedulerRunJobInterval: 100 * time.Millisecond}) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + + hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "plugin", proj.GetPublicId(), + hostcatalogs.WithPluginId("pl_1234567890")) + require.NotNil(hc) + + hset, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id, hostsets.WithAttributes(map[string]interface{}{ + "host_info": []interface{}{ + map[string]interface{}{ + "external_id": "test1", + "ip_addresses": []string{"10.0.0.1", "192.168.1.1"}, + "dns_names": []string{"foo.hashicorp.com", "boundaryproject.io"}, + }, + map[string]interface{}{ + "external_id": "test2", + "ip_addresses": []string{"10.0.0.2", "192.168.1.2"}, + "dns_names": []string{"foo2.hashicorp.com", "boundaryproject2.io"}, + }, + }, + })) + require.NoError(err) + require.NotNil(hset) + time.Sleep(1*time.Second) + + hClient := hosts.NewClient(client) + hl, err := hClient.List(tc.Context(), hc.Item.Id) + require.NoError(err) + require.Len(hl.Items, 2) + + h, err := hClient.Read(tc.Context(), hl.Items[0].Id) + require.NoError(err) + assert.Equal(hl.Items[0], h.Item) + + _, err = hClient.Update(tc.Context(), h.Item.Id, h.Item.Version, hosts.WithName("foo")) + require.Error(err) + _, err = hClient.Delete(tc.Context(), h.Item.Id) + require.Error(err) +} + func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) diff --git a/internal/tests/api/hostsets/host_set_test.go b/internal/tests/api/hostsets/host_set_test.go index eb78af142..3316e09f3 100644 --- a/internal/tests/api/hostsets/host_set_test.go +++ b/internal/tests/api/hostsets/host_set_test.go @@ -102,6 +102,57 @@ func TestList(t *testing.T) { assert.Equal(filterItem.Id, ul.Items[0].Id) } +func TestList_Plugin(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + + hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "plugin", proj.GetPublicId(), + hostcatalogs.WithPluginId("pl_1234567890")) + require.NoError(err) + require.NotNil(hc) + + hClient := hostsets.NewClient(client) + + ul, err := hClient.List(tc.Context(), hc.Item.Id) + require.NoError(err) + assert.Empty(ul.Items) + + var expected []*hostsets.HostSet + for i := 0; i < 10; i++ { + expected = append(expected, &hostsets.HostSet{Name: fmt.Sprint(i)}) + } + + hcr, err := hClient.Create(tc.Context(), hc.Item.Id, hostsets.WithName(expected[0].Name)) + require.NoError(err) + expected[0] = hcr.Item + + ul, err = hClient.List(tc.Context(), hc.Item.Id) + require.NoError(err) + assert.ElementsMatch(comparableSetSlice(expected[:1]), comparableSetSlice(ul.Items)) + + for i := 1; i < 10; i++ { + hcr, err = hClient.Create(tc.Context(), hc.Item.Id, hostsets.WithName(expected[i].Name)) + require.NoError(err) + expected[i] = hcr.Item + } + ul, err = hClient.List(tc.Context(), hc.Item.Id) + require.NoError(err) + assert.ElementsMatch(comparableSetSlice(expected), comparableSetSlice(ul.Items)) + + filterItem := ul.Items[3] + ul, err = hClient.List(tc.Context(), hc.Item.Id, + hostsets.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) + require.NoError(err) + assert.Len(ul.Items, 1) + assert.Equal(filterItem.Id, ul.Items[0].Id) +} + func comparableSetSlice(in []*hostsets.HostSet) []hostsets.HostSet { var filtered []hostsets.HostSet for _, i := range in { diff --git a/sdk/pbs/controller/api/resources/hosts/host.pb.go b/sdk/pbs/controller/api/resources/hosts/host.pb.go index f568bde37..e0dec3b5c 100644 --- a/sdk/pbs/controller/api/resources/hosts/host.pb.go +++ b/sdk/pbs/controller/api/resources/hosts/host.pb.go @@ -57,6 +57,10 @@ type Host struct { HostSetIds []string `protobuf:"bytes,100,rep,name=host_set_ids,proto3" json:"host_set_ids,omitempty"` // The attributes that are applicable to the specific Host type. Attributes *structpb.Struct `protobuf:"bytes,110,opt,name=attributes,proto3" json:"attributes,omitempty"` + // Output only. The list of ip addresses associated with this host. + IpAddresses []string `protobuf:"bytes,120,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` + // Output only. The list of dns addresses associated with this host. + DnsNames []string `protobuf:"bytes,130,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` // Output only. The available actions on this resource for this user. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` } @@ -177,6 +181,20 @@ func (x *Host) GetAttributes() *structpb.Struct { return nil } +func (x *Host) GetIpAddresses() []string { + if x != nil { + return x.IpAddresses + } + return nil +} + +func (x *Host) GetDnsNames() []string { + if x != nil { + return x.DnsNames + } + return nil +} + func (x *Host) GetAuthorizedActions() []string { if x != nil { return x.AuthorizedActions @@ -254,7 +272,7 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{ 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, - 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbc, + 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x05, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, @@ -295,23 +313,27 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{ 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, - 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75, 0x0a, - 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x12, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, - 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, + 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x78, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x82, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2f, 0x0a, + 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75, + 0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, + 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f, + 0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From ec36528e97d7aba481f7f3216924523ab1ac4697 Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 2 Nov 2021 17:49:04 -0700 Subject: [PATCH 45/71] Updating dependencies since the host plugins were updated. (#1649) --- go.mod | 16 ++++++++-------- go.sum | 27 ++++++++++++++++----------- plugins/host/mains/aws/go.mod | 4 ++-- plugins/host/mains/aws/go.sum | 12 ++++++------ plugins/host/mains/azure/go.mod | 4 ++-- plugins/host/mains/azure/go.sum | 12 ++++++------ 6 files changed, 40 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 7b1e0a1ab..a2e634bb1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ replace github.com/hashicorp/boundary/sdk => ./sdk require ( github.com/armon/go-metrics v0.3.9 github.com/bufbuild/buf v0.37.0 - github.com/fatih/color v1.12.0 + github.com/fatih/color v1.13.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.3.0 github.com/godbus/dbus/v5 v5.0.4 // indirect @@ -20,7 +20,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hashicorp/boundary/api v0.0.19 - github.com/hashicorp/boundary/sdk v0.0.12-0.20211007161358-65f3725bf3e1 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 @@ -33,7 +33,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-rootcerts v1.0.2 - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 @@ -54,7 +54,7 @@ require ( github.com/jefferai/keyring v1.1.7-0.20210105022822-8749b3d9ce79 github.com/kr/pretty v0.3.0 github.com/kr/text v0.2.0 - github.com/mattn/go-colorable v0.1.8 + github.com/mattn/go-colorable v0.1.11 github.com/mitchellh/cli v1.1.2 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/go-wordwrap v1.0.1 @@ -72,7 +72,7 @@ require ( github.com/zalando/go-keyring v0.1.1 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 - golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 + golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef golang.org/x/term v0.0.0-20210916214954-140adaaadfaf golang.org/x/tools v0.1.6 google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 @@ -88,7 +88,7 @@ require ( require ( cloud.google.com/go v0.65.0 // indirect github.com/AlecAivazis/survey/v2 v2.2.9 // indirect - github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go v58.0.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.21 // indirect @@ -109,7 +109,7 @@ require ( github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f // indirect github.com/apex/log v1.9.0 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go v1.40.44 // indirect + github.com/aws/aws-sdk-go v1.40.55 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect @@ -167,7 +167,7 @@ require ( github.com/klauspost/compress v1.11.7 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/lib/pq v1.8.0 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect diff --git a/go.sum b/go.sum index 63f3fa19d..db0095859 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/AlecAivazis/survey/v2 v2.2.9 h1:LWvJtUswz/W9/zVVXELrmlvdwWcKE60ZAw0FWV9vssk= github.com/AlecAivazis/survey/v2 v2.2.9/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v57.4.0+incompatible h1:uzhy/mTwu4rq6Be/fJJnE82PRx2A2vjGjzsA3aQ6Qu0= -github.com/Azure/azure-sdk-for-go v57.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM= +github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -150,8 +150,8 @@ github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.40.44 h1:kECaYybTWYZY5IKHvQMxbE6Wi5Qrb+7hbkV7zQV3Sg8= -github.com/aws/aws-sdk-go v1.40.44/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.40.55 h1:l5xNBmafEAfdJIIe32QbTLG3j/JscH8fzZqYyMlntuI= +github.com/aws/aws-sdk-go v1.40.55/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= @@ -282,8 +282,9 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.11.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/favadi/protoc-go-inject-tag v1.3.0 h1:JPrmsmc/uBShG85uY5xGZIa5WJ0IaNZn6LZhQR9tIQE= @@ -520,8 +521,8 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.1/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2/go.mod h1:QRJZ7siKie+SZJB9jLbfKrs0Gd0yPWMtbneg0iU1PrY= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4 h1:aCPwC2WPyomaw5gk5ZZ7DPevN8d9DS3igkmq6DxGMjU= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.4/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= @@ -750,8 +751,10 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -761,8 +764,9 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -1291,8 +1295,9 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef h1:fPxZ3Umkct3LZ8gK9nbk+DWDJ9fstZa2grBn+lWVKPs= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI= diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod index 2262ae32e..4a5a029c9 100644 --- a/plugins/host/mains/aws/go.mod +++ b/plugins/host/mains/aws/go.mod @@ -8,8 +8,8 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57 - github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-plugin v1.4.3 // indirect golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum index d46954112..13144cf28 100644 --- a/plugins/host/mains/aws/go.sum +++ b/plugins/host/mains/aws/go.sum @@ -258,17 +258,17 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57 h1:EQmv83A1CTmgeLQPDQ0sRALw2ou5KWmVZ7k/vSqmxII= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211020174159-d9151151fa57/go.mod h1:YZdE92O3RfToEukkgwmobOsW0lqP9obE5qz/MYnJZ5A= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 h1:uiFO3cBZ/y/5XA+gF6wp9OdrIkiZA9mS7ZHLpl3SDsM= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865 h1:+BrufDh4kWjJhNus2jh+3IauUFbPVrIZHE6wWFtgc9w= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865/go.mod h1:5tskIuUqqYaVl83jzRdxdn+R5T7IylgLJLGTq1L3vKg= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 h1:RKYUGplnpDoE/Cj2vupQqUJS/wWBVvmpxEYeEpGL88s= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= diff --git a/plugins/host/mains/azure/go.mod b/plugins/host/mains/azure/go.mod index 6c58f1953..7d77c36b3 100644 --- a/plugins/host/mains/azure/go.mod +++ b/plugins/host/mains/azure/go.mod @@ -3,8 +3,8 @@ module github.com/hashicorp/boundary/plugins/host/mains/azure go 1.16 require ( - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1 - github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a + github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-plugin v1.4.3 // indirect ) diff --git a/plugins/host/mains/azure/go.sum b/plugins/host/mains/azure/go.sum index 90b8f2cb2..07571c337 100644 --- a/plugins/host/mains/azure/go.sum +++ b/plugins/host/mains/azure/go.sum @@ -255,17 +255,17 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1 h1:HJeBFTmjWY76RF53PV6RuDGwskaHzkJ5xsoAN9MvlbY= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211020173844-3e0fa996c2c1/go.mod h1:aXK+G8Cd3TmfGJoSepevgckCkmHYW5tkMw4WI54eP3w= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066 h1:uiFO3cBZ/y/5XA+gF6wp9OdrIkiZA9mS7ZHLpl3SDsM= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211014161407-f01a8060a066/go.mod h1:jP6jIVYOS4WLPxsE3f8290nLMHBvUsbFQT1fEgzJmrY= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a h1:pEMK2MU4n+Xqp322Qd5agXq8FxiBs2PFD2s1OtnBH8o= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a/go.mod h1:SqjllApBoUV47+IzNfdnzr9jXnNjX8kx4yk2Csc7aCM= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 h1:RKYUGplnpDoE/Cj2vupQqUJS/wWBVvmpxEYeEpGL88s= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= From 1ff2968409f32d4de8cd9bb8ebd0baba178a84ab Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 3 Nov 2021 07:24:53 -0700 Subject: [PATCH 46/71] Warehouse contains "Unsupported" host_address for plugin based hosts (#1656) * Populating the warehouse with the host_address from a plugin basd host is unsupported. * Adding test for plugin hosts in the datawarehouse. --- .../oss/postgres/19/03_hostplugin.up.sql | 3 +- .../19/07_wh_session_dimensions.up.sql | 8 ++-- .../sqltest/initdb.d/03_widgets_persona.sql | 44 ++++++++++++++++++- .../tests/wh/host_dimension_views/source.sql | 18 +++++++- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/19/03_hostplugin.up.sql b/internal/db/schema/migrations/oss/postgres/19/03_hostplugin.up.sql index 9bc035e46..ad17f82ca 100644 --- a/internal/db/schema/migrations/oss/postgres/19/03_hostplugin.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/03_hostplugin.up.sql @@ -6,6 +6,7 @@ begin; ├──────────────────┤ │public_id (pk) │ │scope_id (fk) │ + │name │ └──────────────────┘ ┼ ┼ @@ -20,8 +21,6 @@ begin; │name │ │description │ │version │ - │plugin_name │ - │id_prefix │ └──────────────────┘ */ create table plugin_host ( diff --git a/internal/db/schema/migrations/oss/postgres/19/07_wh_session_dimensions.up.sql b/internal/db/schema/migrations/oss/postgres/19/07_wh_session_dimensions.up.sql index 6c8b9daac..cad5e897a 100644 --- a/internal/db/schema/migrations/oss/postgres/19/07_wh_session_dimensions.up.sql +++ b/internal/db/schema/migrations/oss/postgres/19/07_wh_session_dimensions.up.sql @@ -6,8 +6,9 @@ begin; -- resources. While this will return rows where there are host_sets where hosts -- are not in the host sets, this view is only used when looking up a row by -- host, set, and target so the records where hosts and host sets aren't --- related beyond the shared catalog will be ignored. --- TODO: Support a host having multiple addresses. +-- related beyond the shared catalog will be ignored. Retrieving the ip address +-- or dns name of plugin based hosts is not supported in this version of the +-- warehouse. drop view whx_host_dimension_source; create view whx_host_dimension_source as select -- id is the first column in the target view @@ -22,8 +23,7 @@ select -- id is the first column in the target view when ph.public_id is not null then coalesce(ph.description, 'None') else 'Unknown' end as host_description, - -- TODO: update this to be endpoint address... - coalesce(sh.address, 'Unknown') as host_address, + coalesce(sh.address, 'Unsupported') as host_address, hs.public_id as host_set_id, case when shs.public_id is not null then 'static host set' diff --git a/internal/db/sqltest/initdb.d/03_widgets_persona.sql b/internal/db/sqltest/initdb.d/03_widgets_persona.sql index 7ddf2ee73..791f011e2 100644 --- a/internal/db/sqltest/initdb.d/03_widgets_persona.sql +++ b/internal/db/sqltest/initdb.d/03_widgets_persona.sql @@ -236,6 +236,44 @@ begin; static_host_set as s where h.catalog_id = s.catalog_id and h.address like '%.widget'; + + insert into plugin_host + (scope_id, public_id, name) + values + ('global', 'plg___wb-hplg', 'Short Host Plugin'); + + insert into host_plugin_catalog + (scope_id, public_id, plugin_id, name, attributes) + values + ('p____bwidget', 'c___wb-plghcl', 'plg___wb-hplg', 'Big Widget Plugin Catalog', ''), + ('p____swidget', 'c___ws-plghcl', 'plg___wb-hplg', 'Small Widget Plugin Catalog', ''); + + insert into host_plugin_host + (catalog_id, public_id, external_id) + values + ('c___wb-plghcl', 'h_____wb__01-plgh', '1 big widget'), + ('c___wb-plghcl', 'h_____wb__02-plgh', '2 big widget'), + ('c___wb-plghcl', 'h_____wb__03-plgh', '3 big widget'), + + ('c___ws-plghcl', 'h_____ws__01-plgh', '1 small widget'), + ('c___ws-plghcl', 'h_____ws__02-plgh', '2 small widget'), + ('c___ws-plghcl', 'h_____ws__03-plgh', '3 small widget'); + + insert into host_plugin_set + (catalog_id, public_id, name, attributes, need_sync) + values + ('c___wb-plghcl', 's___1wb-plghs', 'Big Widget Plugin Set 1', '', false), + ('c___wb-plghcl', 's___2wb-plghs', 'Big Widget Plugin Set 2', '', false), + ('c___ws-plghcl', 's___1ws-plghs', 'Small Widget Plugin Set 1', '', false), + ('c___ws-plghcl', 's___2ws-plghs', 'Small Widget Plugin Set 2', '', false); + + insert + into host_plugin_set_member + ( host_id, set_id, catalog_id) + select h.public_id, s.public_id, s.catalog_id + from host_plugin_host as h, + host_plugin_set as s + where h.catalog_id = s.catalog_id; end; $$ language plpgsql; @@ -257,7 +295,11 @@ begin; ('t_________wb', 's___1wb-sths'), ('t_________wb', 's___2wb-sths'), ('t_________ws', 's___1ws-sths'), - ('t_________ws', 's___2ws-sths'); + ('t_________ws', 's___2ws-sths'), + ('t_________wb', 's___1wb-plghs'), + ('t_________wb', 's___2wb-plghs'), + ('t_________ws', 's___1ws-plghs'), + ('t_________ws', 's___2ws-plghs'); end; $$ language plpgsql; diff --git a/internal/db/sqltest/tests/wh/host_dimension_views/source.sql b/internal/db/sqltest/tests/wh/host_dimension_views/source.sql index 5c9bc77ae..3334d87fb 100644 --- a/internal/db/sqltest/tests/wh/host_dimension_views/source.sql +++ b/internal/db/sqltest/tests/wh/host_dimension_views/source.sql @@ -1,9 +1,9 @@ -- source tests the whx_host_dimension_source view. begin; - select plan(1); + select plan(2); select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets'); - -- auth_password_account + -- Static hosts select is(s.*, row( 'h_____wb__01', 'static host', 'None', 'None', '1.big.widget', 's___2wb-sths', 'static host set', 'Big Widget Static Set 2', 'None', @@ -17,6 +17,20 @@ begin; and s.host_set_id = 's___2wb-sths' and s.target_id = 't_________wb'; + -- Plugin based hosts + select is(s.*, row( + 'h_____wb__01-plgh', 'plugin host', 'None', 'None', 'Unsupported', + 's___2wb-plghs', 'plugin host set', 'Big Widget Plugin Set 2', 'None', + 'c___wb-plghcl', 'plugin host catalog', 'Big Widget Plugin Catalog', 'None', + 't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1, + 'p____bwidget', 'Big Widget Factory', 'None', + 'o_____widget', 'Widget Inc', 'None' + )::whx_host_dimension_source) + from whx_host_dimension_source as s + where s.host_id = 'h_____wb__01-plgh' + and s.host_set_id = 's___2wb-plghs' + and s.target_id = 't_________wb'; + select * from finish(); rollback; From b9269ce93c3a734dd6b807fc5fd50a6e1e1b3449 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 3 Nov 2021 12:39:41 -0400 Subject: [PATCH 47/71] Update available actions for host resource (#1659) --- internal/cmd/commands/dev/dev.go | 2 + internal/cmd/commands/hostscmd/funcs.go | 2 +- .../host_catalogs/host_catalog_service.go | 42 +++++++++------- .../host_catalog_service_test.go | 40 ++++++++------- .../handlers/host_sets/host_set_service.go | 46 +++++++++++------ .../host_sets/host_set_service_test.go | 38 +++++++------- .../controller/handlers/hosts/host_service.go | 28 +++++++---- .../handlers/hosts/host_service_test.go | 50 ++++++++++--------- 8 files changed, 147 insertions(+), 101 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index b6de8cd7c..f8b2fad60 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -10,6 +10,7 @@ import ( "runtime" "strings" "syscall" + "time" "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" @@ -556,6 +557,7 @@ func (c *Command) Run(args []string) int { var opts []base.Option if c.flagCreateLoopbackHostCatalogPlugin { opts = append(opts, base.WithHostPlugin("pl_1234567890", plugin.NewWrappingPluginClient(plugin.NewLoopbackPlugin()))) + c.Config.Controller.SchedulerRunJobInterval = 100 * time.Millisecond } switch c.flagDatabaseUrl { case "": diff --git a/internal/cmd/commands/hostscmd/funcs.go b/internal/cmd/commands/hostscmd/funcs.go index ed6c292d9..95c61b5cb 100644 --- a/internal/cmd/commands/hostscmd/funcs.go +++ b/internal/cmd/commands/hostscmd/funcs.go @@ -56,7 +56,7 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { func (c *Command) printListTable(items []*hosts.Host) string { if len(items) == 0 { - return "No host sets found" + return "No hosts found" } var output []string diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 86360a124..59a3bb1b5 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "google.golang.org/grpc/codes" @@ -50,13 +51,16 @@ var ( action.List, } - collectionTypeMap = map[string]map[resource.Type]action.ActionSet{ - "static": { + collectionTypeMap = map[subtypes.Subtype]map[resource.Type]action.ActionSet{ + static.Subtype: { resource.HostSet: host_sets.CollectionActions, resource.Host: hosts.CollectionActions, }, - "plugin": { + plugin.Subtype: { resource.HostSet: host_sets.CollectionActions, + resource.Host: action.ActionSet{ + action.List, + }, }, } ) @@ -160,15 +164,15 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - var collType string + var subtype subtypes.Subtype switch item.(type) { case *static.HostCatalog: - collType = "static" + subtype = static.Subtype case *plugin.HostCatalog: - collType = "plugin" + subtype = plugin.Subtype } - if collType != "" { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, item.GetPublicId()) + if subtype != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[subtype], authResults.Scope.Id, item.GetPublicId()) if err != nil { return nil, err } @@ -224,15 +228,15 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - var collType string + var subtype subtypes.Subtype switch hc.(type) { case *static.HostCatalog: - collType = "static" + subtype = static.Subtype case *plugin.HostCatalog: - collType = "plugin" + subtype = plugin.Subtype } - if collType != "" { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, hc.GetPublicId()) + if subtype != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[subtype], authResults.Scope.Id, hc.GetPublicId()) if err != nil { return nil, err } @@ -281,15 +285,15 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - var collType string + var subtype subtypes.Subtype switch hc.(type) { case *static.HostCatalog: - collType = "static" + subtype = static.Subtype case *plugin.HostCatalog: - collType = "plugin" + subtype = plugin.Subtype } - if collType != "" { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[collType], authResults.Scope.Id, hc.GetPublicId()) + if subtype != "" { + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[subtype], authResults.Scope.Id, hc.GetPublicId()) if err != nil { return nil, err } @@ -341,7 +345,7 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap["static"], authResults.Scope.Id, hc.GetPublicId()) + collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[static.Subtype], authResults.Scope.Id, hc.GetPublicId()) if err != nil { return nil, err } diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 8574272ff..9e8193ed2 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" @@ -37,8 +38,8 @@ import ( "github.com/stretchr/testify/require" ) -var authorizedCollectionActions = map[string]map[string]*structpb.ListValue{ - "static": { +var authorizedCollectionActions = map[subtypes.Subtype]map[string]*structpb.ListValue{ + static.Subtype: { "host-sets": { Values: []*structpb.Value{ structpb.NewStringValue("create"), @@ -52,13 +53,18 @@ var authorizedCollectionActions = map[string]map[string]*structpb.ListValue{ }, }, }, - "plugin": { + plugin.Subtype: { "host-sets": { Values: []*structpb.Value{ structpb.NewStringValue("create"), structpb.NewStringValue("list"), }, }, + "hosts": { + Values: []*structpb.Value{ + structpb.NewStringValue("list"), + }, + }, }, } @@ -98,7 +104,7 @@ func TestGet_Static(t *testing.T) { UpdatedTime: hc.UpdateTime.GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], } cases := []struct { @@ -200,7 +206,7 @@ func TestGet_Plugin(t *testing.T) { UpdatedTime: hc.UpdateTime.GetTimestamp(), Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["plugin"], + AuthorizedCollectionActions: authorizedCollectionActions[plugin.Subtype], } cases := []struct { @@ -291,7 +297,7 @@ func TestList(t *testing.T) { Version: 1, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }) } @@ -315,7 +321,7 @@ func TestList(t *testing.T) { Version: 1, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["plugin"], + AuthorizedCollectionActions: authorizedCollectionActions[plugin.Subtype], } wantSomeCatalogs = append(wantSomeCatalogs, cat) testPluginCatalogs = append(testPluginCatalogs, cat) @@ -332,7 +338,7 @@ func TestList(t *testing.T) { Version: 1, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }) } @@ -355,7 +361,7 @@ func TestList(t *testing.T) { Version: 1, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["plugin"], + AuthorizedCollectionActions: authorizedCollectionActions[plugin.Subtype], }) } @@ -685,7 +691,7 @@ func TestCreate_Static(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -853,7 +859,7 @@ func TestCreate_Plugin(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["plugin"], + AuthorizedCollectionActions: authorizedCollectionActions[plugin.Subtype], }, }, }, @@ -1041,7 +1047,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -1067,7 +1073,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -1122,7 +1128,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -1145,7 +1151,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -1170,7 +1176,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, @@ -1195,7 +1201,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hc.GetCreateTime().GetTimestamp(), Type: "static", AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: authorizedCollectionActions["static"], + AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype], }, }, }, diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index a56a569ec..4d44777e5 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/go-secure-stdlib/strutil" @@ -35,14 +36,22 @@ var ( // IdActions contains the set of actions that can be performed on // individual resources - IdActions = action.ActionSet{ - action.NoOp, - action.Read, - action.Update, - action.Delete, - action.AddHosts, - action.SetHosts, - action.RemoveHosts, + idActionsTypeMap = map[subtypes.Subtype]action.ActionSet{ + static.Subtype: action.ActionSet{ + action.NoOp, + action.Read, + action.Update, + action.Delete, + action.AddHosts, + action.SetHosts, + action.RemoveHosts, + }, + plugin.Subtype: action.ActionSet{ + action.NoOp, + action.Read, + action.Update, + action.Delete, + }, } // CollectionActions contains the set of actions that can be performed on @@ -115,7 +124,8 @@ func (s Service) ListHostSetsWithOptions(ctx context.Context, req *pbs.ListHostS } for _, item := range hl { res.Id = item.GetPublicId() - authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings() + idActions := idActionsTypeMap[host.SubtypeFromId(res.Id)] + authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), idActions, auth.WithResource(&res)).Strings() if len(authorizedActions) == 0 { continue } @@ -172,7 +182,8 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } if plg != nil { outputOpts = append(outputOpts, handlers.WithPlugin(plg)) @@ -213,7 +224,8 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetItem().GetHostCatalogId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } if plg != nil { outputOpts = append(outputOpts, handlers.WithPlugin(plg)) @@ -257,7 +269,8 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, hs, hosts, outputOpts...) @@ -311,7 +324,8 @@ func (s Service) AddHostSetHosts(ctx context.Context, req *pbs.AddHostSetHostsRe outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, hs, hosts, outputOpts...) @@ -348,7 +362,8 @@ func (s Service) SetHostSetHosts(ctx context.Context, req *pbs.SetHostSetHostsRe outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, hs, hosts, outputOpts...) @@ -386,7 +401,8 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, hs, hosts, outputOpts...) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index b7c36ecdc..7fac8c530 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" @@ -38,7 +39,10 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -var testAuthorizedActions = []string{"no-op", "read", "update", "delete", "add-hosts", "set-hosts", "remove-hosts"} +var testAuthorizedActions = map[subtypes.Subtype][]string{ + static.Subtype: {"no-op", "read", "update", "delete", "add-hosts", "set-hosts", "remove-hosts"}, + plugin.Subtype: {"no-op", "read", "update", "delete"}, +} func TestGet_Static(t *testing.T) { t.Parallel() @@ -78,7 +82,7 @@ func TestGet_Static(t *testing.T) { Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], } cases := []struct { @@ -181,7 +185,7 @@ func TestGet_Plugin(t *testing.T) { Description: plg.GetDescription(), }, PreferredEndpoints: prefEndpoints, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], } cases := []struct { @@ -272,7 +276,7 @@ func TestList_Static(t *testing.T) { UpdatedTime: h.GetUpdateTime().GetTimestamp(), Version: h.GetVersion(), Type: static.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }) } @@ -383,7 +387,7 @@ func TestList_Plugin(t *testing.T) { UpdatedTime: h.GetUpdateTime().GetTimestamp(), Version: h.GetVersion(), Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], PreferredEndpoints: preferredEndpoints, }) } @@ -683,7 +687,7 @@ func TestCreate_Static(t *testing.T) { Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, Type: "static", - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -712,7 +716,7 @@ func TestCreate_Static(t *testing.T) { Name: &wrappers.StringValue{Value: "no type name"}, Description: &wrappers.StringValue{Value: "no type desc"}, Type: "static", - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -864,7 +868,7 @@ func TestCreate_Plugin(t *testing.T) { Name: &wrappers.StringValue{Value: "No Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], }, }, }, @@ -890,7 +894,7 @@ func TestCreate_Plugin(t *testing.T) { Name: &wrappers.StringValue{Value: "With Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], Attributes: testAttrs, }, }, @@ -918,7 +922,7 @@ func TestCreate_Plugin(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), PreferredEndpoints: prefEndpoints, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], }, }, }, @@ -963,7 +967,7 @@ func TestCreate_Plugin(t *testing.T) { Name: &wrappers.StringValue{Value: "no type name"}, Description: &wrappers.StringValue{Value: "no type desc"}, Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], }, }, }, @@ -1114,7 +1118,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1140,7 +1144,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1209,7 +1213,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1232,7 +1236,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1257,7 +1261,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1282,7 +1286,7 @@ func TestUpdate(t *testing.T) { CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", HostIds: hIds, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, diff --git a/internal/servers/controller/handlers/hosts/host_service.go b/internal/servers/controller/handlers/hosts/host_service.go index e62cfffc7..70ea91358 100644 --- a/internal/servers/controller/handlers/hosts/host_service.go +++ b/internal/servers/controller/handlers/hosts/host_service.go @@ -33,11 +33,17 @@ var ( // IdActions contains the set of actions that can be performed on // individual resources - IdActions = action.ActionSet{ - action.NoOp, - action.Read, - action.Update, - action.Delete, + idActionsTypeMap = map[subtypes.Subtype]action.ActionSet{ + static.Subtype: { + action.NoOp, + action.Read, + action.Update, + action.Delete, + }, + plugin.Subtype: { + action.NoOp, + action.Read, + }, } // CollectionActions contains the set of actions that can be performed on @@ -106,7 +112,8 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs } for _, item := range hl { res.Id = item.GetPublicId() - authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings() + idActions := idActionsTypeMap[host.SubtypeFromId(res.Id)] + authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), idActions, auth.WithResource(&res)).Strings() if len(authorizedActions) == 0 { continue } @@ -171,7 +178,8 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings())) } var hostSetIds []string @@ -214,7 +222,8 @@ func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*p outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetItem().GetHostCatalogId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, h, nil, outputOpts...) @@ -255,7 +264,8 @@ func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*p outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings())) + idActions := idActionsTypeMap[host.SubtypeFromId(req.GetId())] + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings())) } item, err := toProto(ctx, h, nil, outputOpts...) diff --git a/internal/servers/controller/handlers/hosts/host_service_test.go b/internal/servers/controller/handlers/hosts/host_service_test.go index 61b7d58fd..93dbe0d2d 100644 --- a/internal/servers/controller/handlers/hosts/host_service_test.go +++ b/internal/servers/controller/handlers/hosts/host_service_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts" "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" @@ -37,7 +38,10 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" ) -var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} +var testAuthorizedActions = map[subtypes.Subtype][]string{ + static.Subtype: {"no-op", "read", "update", "delete"}, + plugin.Subtype: {"no-op", "read"}, +} func TestGet_Static(t *testing.T) { t.Parallel() @@ -73,7 +77,7 @@ func TestGet_Static(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue(h.GetAddress()), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], } cases := []struct { @@ -158,19 +162,19 @@ func TestGet_Plugin(t *testing.T) { plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h}) pHost := &pb.Host{ - HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), - CreatedTime: h.CreateTime.GetTimestamp(), - UpdatedTime: h.UpdateTime.GetTimestamp(), - Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, - Type: plugin.Subtype.String(), + HostCatalogId: hc.GetPublicId(), + Id: h.GetPublicId(), + CreatedTime: h.CreateTime.GetTimestamp(), + UpdatedTime: h.UpdateTime.GetTimestamp(), + Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, + Type: plugin.Subtype.String(), Plugin: &plugins.PluginInfo{ Id: plg.GetPublicId(), Name: plg.GetName(), Description: plg.GetDescription(), }, - HostSetIds: []string{hs.GetPublicId()}, - AuthorizedActions: testAuthorizedActions, + HostSetIds: []string{hs.GetPublicId()}, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], } cases := []struct { @@ -260,7 +264,7 @@ func TestList_Static(t *testing.T) { Type: static.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue(h.GetAddress()), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }) } @@ -358,8 +362,8 @@ func TestList_Plugin(t *testing.T) { h := plugin.TestHost(t, conn, hc.GetPublicId(), fmt.Sprintf("host %d", i)) plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h}) wantHs = append(wantHs, &pb.Host{ - Id: h.GetPublicId(), - HostCatalogId: h.GetCatalogId(), + Id: h.GetPublicId(), + HostCatalogId: h.GetCatalogId(), Plugin: &plugins.PluginInfo{ Id: plg.GetPublicId(), Name: plg.GetName(), @@ -368,10 +372,10 @@ func TestList_Plugin(t *testing.T) { Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, CreatedTime: h.GetCreateTime().GetTimestamp(), UpdatedTime: h.GetUpdateTime().GetTimestamp(), - HostSetIds: []string{hs.GetPublicId()}, + HostSetIds: []string{hs.GetPublicId()}, Version: 1, Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], }) } sort.Slice(wantHs, func(i, j int) bool { @@ -620,7 +624,7 @@ func TestCreate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("123.456.789"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -687,7 +691,7 @@ func TestCreate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("123.456.789"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -829,7 +833,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -857,7 +861,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -927,7 +931,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -952,7 +956,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -979,7 +983,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, @@ -1006,7 +1010,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "address": structpb.NewStringValue("defaultaddress"), }}, - AuthorizedActions: testAuthorizedActions, + AuthorizedActions: testAuthorizedActions[static.Subtype], }, }, }, From 1bc9a07316590a656f96f7403fd4f8418e726107 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Wed, 3 Nov 2021 13:16:45 -0700 Subject: [PATCH 48/71] populate correct field for host catalog secret (#1661) --- internal/host/plugin/host_catalog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/host/plugin/host_catalog.go b/internal/host/plugin/host_catalog.go index 05320c550..8a29da5f8 100644 --- a/internal/host/plugin/host_catalog.go +++ b/internal/host/plugin/host_catalog.go @@ -133,7 +133,7 @@ func (agg *catalogAgg) toCatalogAndPersisted() (*HostCatalog, *HostCatalogSecret if len(agg.Secret) > 0 { s = allocHostCatalogSecret() s.CatalogId = agg.PublicId - s.Secret = agg.Secret + s.CtSecret = agg.Secret s.KeyId = agg.KeyId s.CreateTime = agg.PersistedCreateTime s.UpdateTime = agg.PersistedUpdateTime From a37e4f08c8aaf85ed6b6f996f04e3e2beb350c9e Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 3 Nov 2021 21:22:57 -0400 Subject: [PATCH 49/71] Update aws plugin --- plugins/host/mains/aws/go.mod | 2 +- plugins/host/mains/aws/go.sum | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod index 4a5a029c9..5c590fda2 100644 --- a/plugins/host/mains/aws/go.mod +++ b/plugins/host/mains/aws/go.mod @@ -8,7 +8,7 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865 + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-plugin v1.4.3 // indirect diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum index 13144cf28..2ceb12714 100644 --- a/plugins/host/mains/aws/go.sum +++ b/plugins/host/mains/aws/go.sum @@ -100,7 +100,7 @@ github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f h1:oRD github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -108,8 +108,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.40.55 h1:l5xNBmafEAfdJIIe32QbTLG3j/JscH8fzZqYyMlntuI= -github.com/aws/aws-sdk-go v1.40.55/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.41.16 h1:rNbPreEO4K2b8LwhHOe5+iNRH3Ih35L+H9U5tLdcevo= +github.com/aws/aws-sdk-go v1.41.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -204,7 +204,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -258,8 +257,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865 h1:+BrufDh4kWjJhNus2jh+3IauUFbPVrIZHE6wWFtgc9w= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211029205622-bea322df6865/go.mod h1:5tskIuUqqYaVl83jzRdxdn+R5T7IylgLJLGTq1L3vKg= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e h1:G4C/kxlAnJdas2dUfLKiQlR7MHIhia+zr+Aghj1S52Q= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e/go.mod h1:p41IT5ux9TOshMDpS2s21HW2ShBgcqAn8kC4Ee9U5uQ= github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54= github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -326,13 +325,15 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= -github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= +github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniyEi5CM+FWGY= +github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= @@ -382,7 +383,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -412,8 +412,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -507,7 +508,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/yandex-cloud/go-genproto v0.0.0-20200722140432-762fe965ce77/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20200722140627-2194e5077f13/go.mod h1:LEdAMqa1v/7KYe4b13ALLkonuDxLph57ibUb50ctvJk= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -515,8 +517,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= -github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.9.1 h1:viqrgQwFl5UpSxc046qblj78wZXVDFnSOufaOTER+cc= +github.com/zclconf/go-cty v1.9.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -579,7 +581,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 54bccf36749df2a83634d9e316283366c8e0efed Mon Sep 17 00:00:00 2001 From: Todd Date: Thu, 4 Nov 2021 10:43:08 -0700 Subject: [PATCH 50/71] Mask manager can now include mask paths w/ a specified prefix which would be filtered otherwise (#1654) * Update the mask manager to always allow fields containing a prefix which matchs a passedThroughPrefix. This will be useful when updating plugin based host catalog and sets since attributes and secrets cannot be contained in the mask manager mapping. --- .../controller/handlers/mask_manager.go | 29 +++++++++++++++---- .../controller/handlers/mask_manager_test.go | 5 ++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/servers/controller/handlers/mask_manager.go b/internal/servers/controller/handlers/mask_manager.go index 2f319c5a4..ef3f9d26a 100644 --- a/internal/servers/controller/handlers/mask_manager.go +++ b/internal/servers/controller/handlers/mask_manager.go @@ -19,8 +19,9 @@ type ( MaskSource []protoreflect.ProtoMessage ) -// NewMaskManager returns a mask manager that can translate field masks into the first proto from all subsequent -// protos assuming they are both using the mask_mapping custom option. Error is returned if no mappings are +// NewMaskManager returns a mask manager that can translate field masks into +// the first proto from all subsequent protos assuming they are both using the +// mask_mapping custom option. Error is returned if no mappings are // found or if one of the passed protos has a mapping that doesn't reciprocate. func NewMaskManager(dest MaskDestination, src MaskSource) (MaskManager, error) { const op = "handlers.NewMaskManager" @@ -76,14 +77,32 @@ func mapFromProto(ps []protoreflect.ProtoMessage) (map[string]string, error) { return mapping, nil } -// Translate takes a field mask's paths and returns paths translated for the destination's protobuf. -func (m MaskManager) Translate(paths []string) []string { +// Translate takes a field mask's paths and returns paths translated for the +// destination's protobuf. If a translation doesn't exist for a specific path +// entry then nothing is returned for that specific path entry unless +// passedThroughPrefix is used and applies to that entry. +// +// passedThroughPrefix is helpful when the field mask's paths cannot be mapped +// through the mask mapper for some reason (we don't know the mapping at compile +// time, for example) but we still want certain paths to be passed on. If a +// path entry isn't mapped to anything by the MaskManager but does contain +// a prefix which matches a passedThroughPrefix it will be added unmodified +// to the returned value. +func (m MaskManager) Translate(paths []string, passedThroughPrefix ...string) []string { var result []string for _, v := range paths { vSplit := strings.Split(v, ",") for _, v := range vSplit { - if ov, ok := m[strings.TrimSpace(v)]; ok { + candidate := strings.TrimSpace(v) + if ov, ok := m[candidate]; ok { result = append(result, ov) + } else { + for _, pre := range passedThroughPrefix { + if strings.HasPrefix(candidate, pre) { + result = append(result, candidate) + break + } + } } } } diff --git a/internal/servers/controller/handlers/mask_manager_test.go b/internal/servers/controller/handlers/mask_manager_test.go index 1948a1280..a0638be73 100644 --- a/internal/servers/controller/handlers/mask_manager_test.go +++ b/internal/servers/controller/handlers/mask_manager_test.go @@ -17,6 +17,11 @@ func TestMaskManager(t *testing.T) { assert.Equal(t, []string(nil), mm.Translate([]string{"FiRsT_fIeLd"})) assert.Equal(t, []string{"other_second_field"}, mm.Translate([]string{"strangly_formatted_field"})) assert.Equal(t, []string{"other_second_field", "other_field_3"}, mm.Translate([]string{"strangly_formatted_field", "field3"})) + + // the passedThroughPrefix allows fields to be passed through unmodified + assert.Equal(t, []string{"OtherFirstField", "attributes.doesnt_exist"}, mm.Translate([]string{"first_field", "attributes.doesnt_exist"}, "attributes.")) + // the passedThroughPrefix is ignored if the field already matches a mapped value + assert.Equal(t, []string{"other_second_field", "other_field_3"}, mm.Translate([]string{"strangly_formatted_field", "field3"}, "strangely_")) } func TestMaskManager_Split(t *testing.T) { From 165cc5ef1b501f225f304976adcfc6902c273fba Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Thu, 4 Nov 2021 17:09:32 -0700 Subject: [PATCH 51/71] fix ineffectual assignment to err in DeleteCatalog (#1664) Was being caught by my linters. Looks like the error wasn't being checked after getCatalog. --- internal/host/plugin/repository_host_catalog.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 40aa5302c..6007106d3 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -216,6 +216,10 @@ func (r *Repository) DeleteCatalog(ctx context.Context, id string, _ ...Option) return db.NoRowsAffected, errors.Wrap(ctx, err, op) } sets, _, err := r.getSets(ctx, "", c.GetPublicId()) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + var plgSets []*pbset.HostSet for _, s := range sets { ps, err := toPluginSet(ctx, s) From 0670e272e3160c40dc37b707239448d7938cb94d Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Fri, 5 Nov 2021 10:43:58 -0700 Subject: [PATCH 52/71] internal/host/plugin: Add UpdateCatalog (#1634) * internal/host/plugin: Add UpdateCatalog This adds support for updating plugin-based host catalogs. The workflow is: * The existing catalog is fetched. * Any attribute changes are patched against it, along with other changes such as name and description. * Both the existing catalog, and the state of the catalog as it will exist post-update, along with any persisted secrets, are sent to the plugin via an OnUpdateCatalog request. Upon success, the following is done: * The record is updated if non-secret updates need to be made. * Secrets are updated if they still exist. * If an empty secret set was sent back, secrets are instead deleted. * If the secrets field was instead null, secret update is skipped altogether. There has also been a lot of helper code added to the tests to help make the update tests more readable and reduce boilerplate. While the create tests have not been updated to use these new helpers, it might be a good idea to do so for consistency. * move secret/persisted state closer to plugin call * Update internal/host/plugin/repository_host_catalog.go Co-authored-by: Todd * Update internal/host/plugin/repository_host_catalog_test.go Co-authored-by: Todd * move all test helpers for UpdateCatalog inline * WIP - transparent attributes struct/wire conversion * Revert "WIP - transparent attributes struct/wire conversion" This reverts commit 31af1b1b7e535119aa23bd0aa936da1a80eb5aa2. * deal with multiple attribute masks * Note TODO for upsertQuery for HostCatalogSecret * add version check guard * Update internal/host/plugin/host_catalog_secret.go Co-authored-by: Todd * remove redundant JIRA reference * don't separate number of updated catalogs and secrets * increment version when secrets is the only thing being updated * add another test for updating both catalog and secrets at once * correct comment for mock plugin manager section * add first-part path matching for secrets * update plugin lookup error to return errors.Internal * only add name/description to plugin request if they are non-empty * update duplicate tests to use two project scopes * remove unused test checks * return plugin record along with host catalog in update * move away from using passed in catalog after patching newCatalog * use t.Cleanup() instead of defer Co-authored-by: Todd --- internal/host/plugin/host_catalog_secret.go | 7 +- .../host/plugin/host_catalog_secret_test.go | 4 +- .../host/plugin/repository_host_catalog.go | 312 +++++++- .../plugin/repository_host_catalog_test.go | 751 +++++++++++++++++- 4 files changed, 1062 insertions(+), 12 deletions(-) diff --git a/internal/host/plugin/host_catalog_secret.go b/internal/host/plugin/host_catalog_secret.go index 7daf0abf3..84eda4867 100644 --- a/internal/host/plugin/host_catalog_secret.go +++ b/internal/host/plugin/host_catalog_secret.go @@ -90,7 +90,11 @@ func (c *HostCatalogSecret) decrypt(ctx context.Context, cipher wrapping.Wrapper return nil } -func (c *HostCatalogSecret) insertQuery() (query string, queryValues []interface{}) { +// TODO(ICU-2835): this is necessary as update logic requires a static +// public_id or private_id field. Once post-lookup updates can +// support alternate primary key fields, we can port this to native +// Update and upserts w/OnConflict. +func (c *HostCatalogSecret) upsertQuery() (query string, queryValues []interface{}) { query = upsertHostCatalogSecretQuery queryValues = []interface{}{ sql.Named("catalog_id", c.CatalogId), @@ -118,6 +122,7 @@ func (c *HostCatalogSecret) oplogMessage(opType db.OpType) *oplog.Message { msg.OpType = oplog.OpType_OP_TYPE_CREATE case db.UpdateOp: msg.OpType = oplog.OpType_OP_TYPE_UPDATE + msg.FieldMaskPaths = []string{"secret", "key_id"} case db.DeleteOp: msg.OpType = oplog.OpType_OP_TYPE_DELETE } diff --git a/internal/host/plugin/host_catalog_secret_test.go b/internal/host/plugin/host_catalog_secret_test.go index 9a24404b1..09bb7af02 100644 --- a/internal/host/plugin/host_catalog_secret_test.go +++ b/internal/host/plugin/host_catalog_secret_test.go @@ -135,7 +135,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { &structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}) require.NoError(t, err) assert.NoError(t, hcs.encrypt(ctx, databaseWrapper)) - q, v := hcs.insertQuery() + q, v := hcs.upsertQuery() _, err = rw.Exec(ctx, q, v) assert.NoError(t, err) @@ -153,7 +153,7 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) { &structpb.Struct{Fields: map[string]*structpb.Value{"updated": structpb.NewStringValue("value")}}) require.NoError(t, err) assert.NoError(t, updated.encrypt(ctx, databaseWrapper)) - q, v = updated.insertQuery() + q, v = updated.upsertQuery() _, err = rw.Exec(ctx, q, v) assert.NoError(t, err) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 6007106d3..dda662693 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -3,11 +3,13 @@ package plugin import ( "context" "fmt" + "strings" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/patchstruct" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" @@ -18,6 +20,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" ) // CreateCatalog inserts c into the repository and returns a new @@ -113,7 +116,7 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt if hcSecret != nil { newSecret := hcSecret.clone() - q, v := newSecret.insertQuery() + q, v := newSecret.upsertQuery() rows, err := w.Exec(ctx, q, v) if err != nil { return errors.Wrap(ctx, err, op) @@ -145,6 +148,299 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt return newHostCatalog, plg, nil } +// UpdateCatalog updates the repository entry for c.PublicId with the +// values in c for the fields listed in fieldMask. It returns a new +// HostCatalog containing the updated values and a count of the number of +// records updated. c is not changed. +// +// c must contain a valid PublicId. c.Name, c.Description, and +// c.Attributes can be updated; if c.Secrets is present, its contents +// are sent to the plugin (along with any other changes, see below) +// before the update is sent to the database. +// +// An attribute of c will be set to NULL in the database if the +// attribute in c is the zero value and it is included in fieldMask. +// Note that this does not apply to c.Attributes - a null +// c.Attributes is a no-op for modifications. Rather, if fields need +// to be reset, its field in c.Attributes should individually set to +// null. +// +// Updates are sent to OnUpdateCatalog with a full copy of both the +// current catalog, and the state of the new catalog should it be +// updated, along with any secrets included in the new request. This +// request may alter the returned persisted state. Update of the +// record in the database is aborted if this call fails. +func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version uint32, fieldMask []string, _ ...Option) (*HostCatalog, *hostplugin.Plugin, int, error) { + const op = "plugin.(Repository).UpdateCatalog" + if c == nil { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil HostCatalog") + } + if c.HostCatalog == nil { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostCatalog") + } + if c.PublicId == "" { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + if c.ScopeId == "" { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if len(fieldMask) == 0 { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "empty field mask") + } + + // Get the old catalog first. We patch the record first before + // sending it to the DB for updating so that we can run on + // OnUpdateCatalog. Note that the field masks are still used for + // updating. + currentCatalog, currentCatalogPersisted, err := r.getCatalog(ctx, c.PublicId) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("error looking up catalog with id %q", c.PublicId))) + } + + if currentCatalog == nil { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("catalog with id %q not found", c.PublicId)) + } + + // Assert the version of the current catalog to make sure we're + // updating the correct one. + if currentCatalog.GetVersion() != version { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.VersionMismatch, op, fmt.Sprintf("catalog version mismatch, want=%d, got=%d", currentCatalog.GetVersion(), version)) + } + + // Clone the catalog so that we can set fields. + newCatalog := currentCatalog.clone() + var updateAttributes bool + var dbMask, nullFields []string + for _, f := range fieldMask { + switch { + case strings.EqualFold("name", f) && c.Name == "": + nullFields = append(nullFields, "name") + newCatalog.Name = c.Name + case strings.EqualFold("name", f) && c.Name != "": + dbMask = append(dbMask, "name") + newCatalog.Name = c.Name + case strings.EqualFold("description", f) && c.Description == "": + nullFields = append(nullFields, "description") + newCatalog.Description = c.Description + case strings.EqualFold("description", f) && c.Description != "": + dbMask = append(dbMask, "description") + newCatalog.Description = c.Description + case strings.EqualFold("attributes", strings.Split(f, ".")[0]): + // Flag attributes for updating. While multiple masks may be + // sent, we only need to do this once. + updateAttributes = true + case strings.EqualFold("secrets", strings.Split(f, ".")[0]): + // While in a similar format, secrets are passed along + // wholesale (for the time being). Don't append this mask + // field, as secrets do not have a database entry. Clear the + // secrets out of the working set after. + newCatalog.Secrets = c.Secrets + c.Secrets = nil + + default: + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) + } + } + + if updateAttributes && c.Attributes != nil { + dbMask = append(dbMask, "attributes") + newCatalog.Attributes, err = patchstruct.PatchBytes(newCatalog.Attributes, c.Attributes) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("error in catalog attribute JSON")) + } + } + + // Get the plugin for the host catalog - this is to return it back + // after the update is complete. We don't actually do anything with + // the record otherwise. Fetch it here so that if there's an + // integrity error, we don't call the plugin. + plg, err := r.getPlugin(ctx, currentCatalog.GetPluginId()) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + // Get the plugin client. + plgClient, ok := r.plugins[currentCatalog.GetPluginId()] + if !ok || plgClient == nil { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin %q not available", currentCatalog.GetPluginId())) + } + + // Convert the catalog values to API protobuf values, which is what + // we use for the plugin hook calls. + currPlgHc, err := toPluginCatalog(ctx, currentCatalog) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + newPlgHc, err := toPluginCatalog(ctx, newCatalog) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + plgResp, err := plgClient.OnUpdateCatalog(ctx, &plgpb.OnUpdateCatalogRequest{ + CurrentCatalog: currPlgHc, + NewCatalog: newPlgHc, + Persisted: currentCatalogPersisted, + }) + if err != nil { + if status.Code(err) != codes.Unimplemented { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + } + + // Success for OnUpdateCatalog. This means we can start updating. + // First, check for returned persisted data and encrypt it. + var hcSecret *HostCatalogSecret + var deleteSecrets bool + if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { + if len(plgResp.GetPersisted().GetSecrets().GetFields()) == 0 { + // Flag the secret to be deleted. + hcSecret, err = newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + deleteSecrets = true + } else { + hcSecret, err = newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + dbWrapper, err := r.kms.GetWrapper(ctx, newCatalog.ScopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } + if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + } + } + + // Get the oplog. + oplogWrapper, err := r.kms.GetWrapper(ctx, newCatalog.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var recordUpdated bool + var returnedCatalog *HostCatalog + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + msgs := make([]*oplog.Message, 0, 3) + ticket, err := w.GetTicket(newCatalog) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + if len(dbMask) != 0 || len(nullFields) != 0 { + returnedCatalog = newCatalog.clone() + var cOplogMsg oplog.Message + catalogsUpdated, err := w.Update( + ctx, + returnedCatalog, + dbMask, + nullFields, + db.NewOplogMsg(&cOplogMsg), + db.WithVersion(&version), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if catalogsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog to be deleted, got %d", catalogsUpdated)) + } + msgs = append(msgs, &cOplogMsg) + recordUpdated = true + } else { + // Returned catalog needs to be the old copy, as no fields in the + // catalog itself are being updated (note: secrets may still be + // updated). + returnedCatalog = currentCatalog.clone() + } + + if hcSecret != nil { + if deleteSecrets { + // We didn't set/encrypt the persisted data because there was + // none returned. Just delete the entry. + deletedSecret := hcSecret.clone() + q, v := deletedSecret.deleteQuery() + var err error + secretsUpdated, err := w.Exec(ctx, q, v) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if secretsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog secret to be deleted, got %d", secretsUpdated)) + } + msgs = append(msgs, deletedSecret.oplogMessage(db.DeleteOp)) + } else { + // Update the secrets. + updatedSecret := hcSecret.clone() + q, v := updatedSecret.upsertQuery() + var err error + secretsUpdated, err := w.Exec(ctx, q, v) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if secretsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog secret to be updated, got %d", secretsUpdated)) + } + msgs = append(msgs, updatedSecret.oplogMessage(db.UpdateOp)) + } + + if !recordUpdated { + // we only updated secrets, so we need to increment the + // version of the host catalog manually. + returnedCatalog = newCatalog.clone() + returnedCatalog.Version = uint32(version) + 1 + var cOplogMsg oplog.Message + catalogsUpdated, err := w.Update( + ctx, + returnedCatalog, + []string{"version"}, + []string{}, + db.NewOplogMsg(&cOplogMsg), + db.WithVersion(&version), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if catalogsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog to be updated, got %d", catalogsUpdated)) + } + msgs = append(msgs, &cOplogMsg) + recordUpdated = true + } + } + + if len(msgs) != 0 { + metadata := newCatalog.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + } + + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s: name %s already exists", newCatalog.PublicId, newCatalog.Name))) + } + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", newCatalog.PublicId))) + } + + var numUpdated int + if recordUpdated { + numUpdated = 1 + } + + return returnedCatalog, plg, numUpdated, nil +} + // LookupCatalog returns the HostCatalog for id. Returns nil, nil if no // HostCatalog is found for id. func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option) (*HostCatalog, *hostplugin.Plugin, error) { @@ -322,9 +618,19 @@ func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, err if in == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin") } + var name, description *wrapperspb.StringValue + if inName := in.GetName(); inName != "" { + name = wrapperspb.String(inName) + } + if inDescription := in.GetDescription(); inDescription != "" { + description = wrapperspb.String(inDescription) + } + hc := &pb.HostCatalog{ - Id: in.GetPublicId(), - ScopeId: in.GetScopeId(), + Id: in.GetPublicId(), + ScopeId: in.GetScopeId(), + Name: name, + Description: description, } if in.GetAttributes() != nil { attrs := &structpb.Struct{} diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index e8ac5db4d..e0333af1b 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" @@ -22,6 +23,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" ) func TestRepository_CreateCatalog(t *testing.T) { @@ -368,12 +370,719 @@ func TestRepository_CreateCatalog(t *testing.T) { }) } -func assertPluginBasedPublicId(t *testing.T, prefix, actual string) { - t.Helper() - assert.NotEmpty(t, actual) - parts := strings.Split(actual, "_") - assert.Equalf(t, 2, len(parts), "want 1 '_' in PublicId, got %d in %q", len(parts), actual) - assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) +func TestRepository_UpdateCatalog(t *testing.T) { + ctx := context.Background() + dbConn, _ := db.TestSetup(t, "postgres") + dbRW := db.New(dbConn) + dbWrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, dbConn, dbWrapper) + dbKmsCache := kms.TestKms(t, dbConn, dbWrapper) + _, projectScope := iam.TestScopes(t, iam.TestRepo(t, dbConn, dbWrapper)) + _, projectScopeAlt := iam.TestScopes(t, iam.TestRepo(t, dbConn, dbWrapper)) + + // Define a plugin "manager", basically just a map with a mock + // plugin in it. This also includes functionality to capture the + // state, and set an error and the returned secrets to nil. Note + // that the way that this is set up means that the tests cannot run + // in parallel, but there could be other factors affecting that as + // well. + var setRespSecretsNil bool + var gotOnUpdateCatalogRequest *plgpb.OnUpdateCatalogRequest + var pluginError error + testPlugin := hostplg.TestPlugin(t, dbConn, "test") + testPluginMap := map[string]plgpb.HostPluginServiceClient{ + testPlugin.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnUpdateCatalogFn: func(_ context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) { + gotOnUpdateCatalogRequest = req + respSecrets := req.GetNewCatalog().GetSecrets() + if setRespSecretsNil { + respSecrets = nil + setRespSecretsNil = false + } + + return &plgpb.OnUpdateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: respSecrets}}, pluginError + }, + }, + }, + } + + // Set up two existing catalogs for duplicate tests, one in the + // same scope that we are working in mostly, and another in a + // different one. Both are project scopes. + const ( + testDuplicateCatalogNameProjectScope = "duplicate-catalog-name-project-scope" + testDuplicateCatalogNameAltScope = "duplicate-catalog-name-alt-scope" + ) + + // Project scope + existingProjectScopeCatalog := TestCatalog(t, dbConn, projectScope.PublicId, testPlugin.GetPublicId()) + existingProjectScopeCatalog.Name = testDuplicateCatalogNameProjectScope + numCatUpdated, err := dbRW.Update(ctx, existingProjectScopeCatalog, []string{"name"}, []string{}) + require.NoError(t, err) + require.Equal(t, 1, numCatUpdated) + + // Alternate scope + existingAltScopeCatalog := TestCatalog(t, dbConn, projectScopeAlt.PublicId, testPlugin.GetPublicId()) + existingAltScopeCatalog.Name = testDuplicateCatalogNameAltScope + numCatUpdated, err = dbRW.Update(ctx, existingAltScopeCatalog, []string{"name"}, []string{}) + require.NoError(t, err) + require.Equal(t, 1, numCatUpdated) + + // Define some helpers here to make the test table more readable. + type changeHostCatalogFunc func(c *HostCatalog) *HostCatalog + + changePublicId := func(s string) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.PublicId = s + return c + } + } + + changeScopeId := func(s string) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.ScopeId = s + return c + } + } + + changeName := func(s string) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.Name = s + return c + } + } + + changeDescription := func(s string) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.Description = s + return c + } + } + + changeAttributes := func(m map[string]interface{}) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.Attributes = mustMarshal(m) + return c + } + } + + changeSecrets := func(m map[string]interface{}) changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.Secrets = mustStruct(m) + return c + } + } + + changeCatalogToNil := func() changeHostCatalogFunc { + return func(_ *HostCatalog) *HostCatalog { + return nil + } + } + + changeEmbeddedCatalogToNil := func() changeHostCatalogFunc { + return func(c *HostCatalog) *HostCatalog { + c.HostCatalog = nil + return c + } + } + + // Define some checks that will be used in the below tests. Some of + // these are re-used, so we define them here. Most of these are + // assertions and no particular one is non-fatal in that they will + // stop execution. Note that these are executed after wantIsErr, so + // if that is set in an individual table test, these will not be + // executed. + // + // Note that we define some state here, similar to how we + // previously defined gotOnUpdateCatalogRequest above next the + // plugin map. + type checkFunc func(t *testing.T, ctx context.Context) + var ( + gotCatalog *HostCatalog + gotNumUpdated int + ) + + checkName := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotCatalog.Name) + } + } + + checkDescription := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotCatalog.Description) + } + } + + checkVersion := func(want uint32) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotCatalog.Version) + } + } + + checkAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + require := require.New(t) + st := &structpb.Struct{} + require.NoError(proto.Unmarshal(gotCatalog.Attributes, st)) + assert.Empty(cmp.Diff(mustStruct(want), st, protocmp.Transform())) + } + } + + checkSecrets := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + require := require.New(t) + + cSecret := allocHostCatalogSecret() + err := dbRW.LookupWhere(ctx, &cSecret, "catalog_id=?", gotCatalog.GetPublicId()) + require.NoError(err) + require.Empty(cSecret.Secret) + require.NotEmpty(cSecret.CtSecret) + + dbWrapper, err := dbKmsCache.GetWrapper(ctx, gotCatalog.GetScopeId(), kms.KeyPurposeDatabase) + require.NoError(err) + require.NoError(cSecret.decrypt(ctx, dbWrapper)) + + st := &structpb.Struct{} + require.NoError(proto.Unmarshal(cSecret.Secret, st)) + assert.Empty(cmp.Diff(mustStruct(want), st, protocmp.Transform())) + } + } + + checkSecretsDeleted := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + + cSecret := allocHostCatalogSecret() + err := dbRW.LookupWhere(ctx, &cSecret, "catalog_id=?", gotCatalog.GetPublicId()) + assert.Error(err) + assert.True(errors.IsNotFoundError(err)) + } + } + + checkUpdateCatalogRequestCurrentNameNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateCatalogRequest.CurrentCatalog.Name) + } + } + + checkUpdateCatalogRequestNewName := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(wrapperspb.String(want), gotOnUpdateCatalogRequest.NewCatalog.Name) + } + } + + checkUpdateCatalogRequestNewNameNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateCatalogRequest.NewCatalog.Name) + } + } + + checkUpdateCatalogRequestCurrentDescriptionNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateCatalogRequest.CurrentCatalog.Description) + } + } + + checkUpdateCatalogRequestNewDescription := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(wrapperspb.String(want), gotOnUpdateCatalogRequest.NewCatalog.Description) + } + } + + checkUpdateCatalogRequestCurrentAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateCatalogRequest.CurrentCatalog.Attributes, protocmp.Transform())) + } + } + + checkUpdateCatalogRequestNewAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateCatalogRequest.NewCatalog.Attributes, protocmp.Transform())) + } + } + + checkUpdateCatalogRequestPersistedSecrets := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateCatalogRequest.Persisted.Secrets, protocmp.Transform())) + } + } + + checkUpdateCatalogRequestSecrets := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateCatalogRequest.NewCatalog.Secrets, protocmp.Transform())) + // Ensure that the current catalog's secrets value is always zero + assert.Zero(gotOnUpdateCatalogRequest.CurrentCatalog.Secrets) + } + } + + checkNumUpdated := func(want int) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotNumUpdated) + } + } + + checkVerifyCatalogOplog := func(op oplog.OpType) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.NoError( + db.TestVerifyOplog( + t, + dbRW, + gotCatalog.PublicId, + db.WithOperation(op), + db.WithCreateNotBefore(10*time.Second), + ), + ) + } + } + + tests := []struct { + name string + withEmptyPluginMap bool + withRespSecretsNil bool + withPluginError error + changeFuncs []changeHostCatalogFunc + version uint32 + fieldMask []string + wantCheckFuncs []checkFunc + wantIsErr errors.Code + }{ + { + name: "nil catalog", + changeFuncs: []changeHostCatalogFunc{changeCatalogToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil embedded catalog", + changeFuncs: []changeHostCatalogFunc{changeEmbeddedCatalogToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing public id", + changeFuncs: []changeHostCatalogFunc{changePublicId("")}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing scope id", + changeFuncs: []changeHostCatalogFunc{changeScopeId("")}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty field mask", + fieldMask: nil, // Should be testing on len + wantIsErr: errors.EmptyFieldMask, + }, + { + name: "bad catalog id", + changeFuncs: []changeHostCatalogFunc{changePublicId("badid")}, + fieldMask: []string{"name"}, + wantIsErr: errors.RecordNotFound, + }, + { + name: "version mismatch", + changeFuncs: []changeHostCatalogFunc{changeName("foo")}, + version: 1, + fieldMask: []string{"name"}, + wantIsErr: errors.VersionMismatch, + }, + { + name: "plugin lookup error", + withEmptyPluginMap: true, + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "plugin invocation error", + withPluginError: errors.New(context.Background(), errors.Internal, "TestRepository_UpdateCatalog/plugin_invocation_error", "test plugin error"), + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "update name (duplicate, same scope)", + changeFuncs: []changeHostCatalogFunc{changeName(testDuplicateCatalogNameProjectScope)}, + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.NotUnique, + }, + { + name: "update name", + changeFuncs: []changeHostCatalogFunc{changeName("foo")}, + version: 2, + fieldMask: []string{"name"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentNameNil(), + checkUpdateCatalogRequestNewName("foo"), + checkName("foo"), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name to same", + changeFuncs: []changeHostCatalogFunc{changeName("")}, + version: 2, + fieldMask: []string{"name"}, + wantCheckFuncs: []checkFunc{ + checkVersion(2), // Version remains same even though row is updated + checkUpdateCatalogRequestCurrentNameNil(), + checkUpdateCatalogRequestNewNameNil(), + checkName(""), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name (duplicate, different scope)", + changeFuncs: []changeHostCatalogFunc{changeName(testDuplicateCatalogNameAltScope)}, + version: 2, + fieldMask: []string{"name"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentNameNil(), + checkUpdateCatalogRequestNewName(testDuplicateCatalogNameAltScope), + checkName(testDuplicateCatalogNameAltScope), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update description", + changeFuncs: []changeHostCatalogFunc{changeDescription("foo")}, + version: 2, + fieldMask: []string{"description"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentDescriptionNil(), + checkUpdateCatalogRequestNewDescription("foo"), + checkDescription("foo"), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (add)", + changeFuncs: []changeHostCatalogFunc{changeAttributes(map[string]interface{}{ + "baz": "qux", + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateCatalogRequestNewAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (overwrite)", + changeFuncs: []changeHostCatalogFunc{changeAttributes(map[string]interface{}{ + "foo": "baz", + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateCatalogRequestNewAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (null)", + changeFuncs: []changeHostCatalogFunc{changeAttributes(map[string]interface{}{ + "foo": nil, + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateCatalogRequestNewAttributes(map[string]interface{}{}), + checkAttributes(map[string]interface{}{}), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (combined)", + changeFuncs: []changeHostCatalogFunc{changeAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + })}, + version: 2, + fieldMask: []string{"attributes.a", "attributes.foo"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateCatalogRequestNewAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update secrets", + changeFuncs: []changeHostCatalogFunc{changeSecrets(map[string]interface{}{ + "three": "four", + })}, + version: 2, + fieldMask: []string{"secrets.three"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkUpdateCatalogRequestSecrets(map[string]interface{}{ + "three": "four", + }), + checkSecrets(map[string]interface{}{ + "three": "four", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update secrets, return nil secrets from plugin", + withRespSecretsNil: true, + changeFuncs: []changeHostCatalogFunc{changeSecrets(map[string]interface{}{ + "three": "four", + })}, + version: 2, + fieldMask: []string{"secrets.three"}, + wantCheckFuncs: []checkFunc{ + checkVersion(2), // no-op + checkUpdateCatalogRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkUpdateCatalogRequestSecrets(map[string]interface{}{ + "three": "four", + }), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(db.NoRowsAffected), + }, + }, + { + name: "delete secrets", + changeFuncs: []changeHostCatalogFunc{changeSecrets(map[string]interface{}{})}, + version: 2, + fieldMask: []string{"secrets"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkUpdateCatalogRequestSecrets(map[string]interface{}{}), + checkSecretsDeleted(), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name and secrets", + changeFuncs: []changeHostCatalogFunc{ + changeName("foo"), + changeSecrets(map[string]interface{}{ + "three": "four", + }), + }, + version: 2, + fieldMask: []string{"name", "secrets.three"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateCatalogRequestCurrentNameNil(), + checkUpdateCatalogRequestNewName("foo"), + checkUpdateCatalogRequestSecrets(map[string]interface{}{ + "three": "four", + }), + checkName("foo"), + checkSecrets(map[string]interface{}{ + "three": "four", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + } + + // Finally define a function for bringing the test subject catalog. + // This function also returns a function to clean up the catalog + // afterwards. + setupHostCatalog := func(t *testing.T, ctx context.Context) *HostCatalog { + t.Helper() + require := require.New(t) + + cat := TestCatalog(t, dbConn, projectScope.PublicId, testPlugin.GetPublicId()) + // Set some (default) attributes on our test catalog + cat.Attributes = mustMarshal(map[string]interface{}{ + "foo": "bar", + }) + + numCatUpdated, err := dbRW.Update(ctx, cat, []string{"attributes"}, []string{}) + require.NoError(err) + require.Equal(1, numCatUpdated) + + // Set up some secrets + cSecretProto := mustStruct(map[string]interface{}{ + "one": "two", + }) + cSecret, err := newHostCatalogSecret(ctx, cat.GetPublicId(), cSecretProto) + require.NoError(err) + scopeWrapper, err := dbKmsCache.GetWrapper(ctx, cat.GetScopeId(), kms.KeyPurposeDatabase) + require.NoError(err) + require.NoError(cSecret.encrypt(ctx, scopeWrapper)) + cSecretQ, cSecretV := cSecret.upsertQuery() + secretsUpdated, err := dbRW.Exec(ctx, cSecretQ, cSecretV) + require.NoError(err) + require.Equal(1, secretsUpdated) + + t.Cleanup(func() { + t.Helper() + assert := assert.New(t) + n, err := dbRW.Delete(ctx, cat) + assert.NoError(err) + assert.Equal(1, n) + }) + + return cat + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + origCat := setupHostCatalog(t, ctx) + + pluginMap := testPluginMap + if tt.withEmptyPluginMap { + pluginMap = make(map[string]plgpb.HostPluginServiceClient) + } + pluginError = tt.withPluginError + t.Cleanup(func() { pluginError = nil }) + repo, err := NewRepository(dbRW, dbRW, dbKmsCache, sched, pluginMap) + require.NoError(err) + require.NotNil(repo) + + workingCat := origCat.clone() + for _, cf := range tt.changeFuncs { + workingCat = cf(workingCat) + } + + setRespSecretsNil = tt.withRespSecretsNil + var gotPlugin *hostplugin.Plugin + gotCatalog, gotPlugin, gotNumUpdated, err = repo.UpdateCatalog(ctx, workingCat, tt.version, tt.fieldMask) + if tt.wantIsErr != 0 { + require.Equal(db.NoRowsAffected, gotNumUpdated) + require.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + return + } + require.NoError(err) + t.Cleanup(func() { gotOnUpdateCatalogRequest = nil }) + + // Quick assertion that the catalog is not nil and that the plugin ID in + // the catalog and the plugin ID in the returned plugin match. Use assert + // as that's what our checks use in the table test's defined checks. + assert.NotNil(gotCatalog) + assert.NotNil(gotPlugin) + assert.Equal(gotCatalog.PluginId, gotPlugin.PublicId) + // Perform checks + for _, check := range tt.wantCheckFuncs { + check(t, ctx) + } + }) + } } func TestRepository_LookupCatalog(t *testing.T) { @@ -628,3 +1337,33 @@ func TestRepository_DeleteCatalogX(t *testing.T) { }) } } + +func assertPluginBasedPublicId(t *testing.T, prefix, actual string) { + t.Helper() + assert.NotEmpty(t, actual) + parts := strings.Split(actual, "_") + assert.Equalf(t, 2, len(parts), "want 1 '_' in PublicId, got %d in %q", len(parts), actual) + assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) +} + +// mustStruct creates a structpb.Struct, and panics if there is an +// error. +func mustStruct(in map[string]interface{}) *structpb.Struct { + out, err := structpb.NewStruct(in) + if err != nil { + panic(err) + } + + return out +} + +// mustMarshal behaves like mustStruct but also converts the Struct +// to wire-format data. +func mustMarshal(in map[string]interface{}) []byte { + b, err := proto.Marshal(mustStruct(in)) + if err != nil { + panic(err) + } + + return b +} From 157f1a091bfd16c8d3ae0b5d05e2cc6ba60e69dc Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Fri, 5 Nov 2021 14:05:52 -0700 Subject: [PATCH 53/71] internal/host/plugin: add update catalog test for empty/same description (#1672) This is to mainly get coverage on the empty description case, but it follows the same logic as the "update name to same" test. --- .../plugin/repository_host_catalog_test.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index e0333af1b..14f96af01 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -612,6 +612,14 @@ func TestRepository_UpdateCatalog(t *testing.T) { } } + checkUpdateCatalogRequestNewDescriptionNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateCatalogRequest.NewCatalog.Description) + } + } + checkUpdateCatalogRequestCurrentAttributes := func(want map[string]interface{}) checkFunc { return func(t *testing.T, ctx context.Context) { t.Helper() @@ -808,6 +816,23 @@ func TestRepository_UpdateCatalog(t *testing.T) { checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, + { + name: "update description to same", + changeFuncs: []changeHostCatalogFunc{changeDescription("")}, + version: 2, + fieldMask: []string{"description"}, + wantCheckFuncs: []checkFunc{ + checkVersion(2), // Version remains same even though row is updated + checkUpdateCatalogRequestCurrentDescriptionNil(), + checkUpdateCatalogRequestNewDescriptionNil(), + checkDescription(""), + checkSecrets(map[string]interface{}{ + "one": "two", + }), + checkNumUpdated(1), + checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, { name: "update attributes (add)", changeFuncs: []changeHostCatalogFunc{changeAttributes(map[string]interface{}{ From 050553ffcf37558353853e57d824cb86f185b90c Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 9 Nov 2021 14:42:48 -0700 Subject: [PATCH 54/71] Add update catalog support for plugin based catalogs in handler and go sdk (#1673) * Create catalog service handler for updating plugin. Add tests for handler and run make fmt. Silently drop attributes where the value is nil when creating the catalog or set. --- .../host/plugin/repository_host_catalog.go | 8 + .../plugin/repository_host_catalog_test.go | 5 +- internal/host/plugin/repository_host_set.go | 10 + .../host/plugin/repository_host_set_test.go | 7 +- internal/libs/patchstruct/patchstruct.go | 5 +- internal/libs/patchstruct/patchstruct_test.go | 45 ++- .../host_catalogs/host_catalog_service.go | 59 +++- .../host_catalog_service_test.go | 282 +++++++++++++++++- .../handlers/host_sets/host_set_service.go | 4 +- .../host_sets/host_set_service_test.go | 14 +- .../api/hostcatalogs/host_catalog_test.go | 11 + internal/tests/api/hosts/host_test.go | 10 +- 12 files changed, 437 insertions(+), 23 deletions(-) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index dda662693..c99e433be 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -60,6 +60,14 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } c.PublicId = id + // Use PatchBytes' functionality that does not add keys where the values + // are nil to the resulting struct since we do not want to store nil valued + // attributes. + c.Attributes, err = patchstruct.PatchBytes([]byte{}, c.Attributes) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + plgHc, err := toPluginCatalog(ctx, c) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 14f96af01..bf9e0c0d2 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -188,7 +188,10 @@ func TestRepository_CreateCatalog(t *testing.T) { ScopeId: prj.GetPublicId(), PluginId: plg.GetPublicId(), Attributes: func() []byte { - st, err := structpb.NewStruct(map[string]interface{}{"k1": "foo"}) + st, err := structpb.NewStruct(map[string]interface{}{ + "k1": "foo", + "nilkey": nil, + }) require.NoError(t, err) b, err := proto.Marshal(st) require.NoError(t, err) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 6e3344244..958b557f1 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/libs/endpoint" + "github.com/hashicorp/boundary/internal/libs/patchstruct" "github.com/hashicorp/boundary/internal/oplog" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" @@ -50,6 +51,15 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, } s = s.clone() + // Use PatchBytes' functionality that does not add keys where the values + // are nil to the resulting struct since we do not want to store nil valued + // attributes. + var err error + s.Attributes, err = patchstruct.PatchBytes([]byte{}, s.Attributes) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + c, per, err := r.getCatalog(ctx, s.CatalogId) if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog")) diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 34fcdfbb8..cc36690d1 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -186,7 +186,12 @@ func TestRepository_CreateSet(t *testing.T) { CatalogId: catalog.PublicId, Description: ("test-description-repo"), Attributes: func() []byte { - b, err := proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"k1": structpb.NewStringValue("foo")}}) + st, err := structpb.NewStruct(map[string]interface{}{ + "k1": "foo", + "removed": nil, + }) + require.NoError(t, err) + b, err := proto.Marshal(st) require.NoError(t, err) return b }(), diff --git a/internal/libs/patchstruct/patchstruct.go b/internal/libs/patchstruct/patchstruct.go index 74798006d..313edb821 100644 --- a/internal/libs/patchstruct/patchstruct.go +++ b/internal/libs/patchstruct/patchstruct.go @@ -71,8 +71,9 @@ func patchM(dst, src map[string]interface{}) map[string]interface{} { // If the value in dst a map, continue to patch dst[k] = patchM(y, x) } else { - // Overwrite - dst[k] = x + // Overwrite after stripping out keys to nil values + newX := patchM(make(map[string]interface{}), x) + dst[k] = newX } default: diff --git a/internal/libs/patchstruct/patchstruct_test.go b/internal/libs/patchstruct/patchstruct_test.go index c043be413..c7a972ba2 100644 --- a/internal/libs/patchstruct/patchstruct_test.go +++ b/internal/libs/patchstruct/patchstruct_test.go @@ -1,8 +1,12 @@ package patchstruct import ( + "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/boundary/internal/host" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" @@ -75,6 +79,24 @@ var testCases = []testCase{ "foo": "bar", }, }, + { + name: "nested with nil", + dst: nil, + src: map[string]interface{}{ + "a": "b", + "nested": map[string]interface{}{ + "c": "d", + "e": nil, + }, + "f": nil, + }, + expected: map[string]interface{}{ + "a": "b", + "nested": map[string]interface{}{ + "c": "d", + }, + }, + }, { name: "overwrite with map in src", dst: map[string]interface{}{ @@ -111,6 +133,17 @@ var testCases = []testCase{ "foo": "bar", }, }, + { + name: "nil dst with src nil value", + dst: nil, + src: map[string]interface{}{ + "foo": "bar", + "fiz": nil, + }, + expected: map[string]interface{}{ + "foo": "bar", + }, + }, } func TestPatchStruct(t *testing.T) { @@ -130,7 +163,11 @@ func TestPatchStruct(t *testing.T) { } actual := PatchStruct(dst, src) - require.Equal(mustStruct(tc.expected), actual) + require.Empty(cmp.Diff(mustStruct(tc.expected), actual, + cmpopts.IgnoreUnexported(structpb.Struct{}, structpb.Value{}), + cmpopts.SortSlices(func(x, y interface{}) bool { + return x.(*host.IpAddress).Address < y.(*host.IpAddress).Address + }))) require.Equal(dstOrig, dst) require.Equal(srcOrig, src) }) @@ -155,12 +192,14 @@ func TestPatchBytesErr(t *testing.T) { t.Run("dst", func(t *testing.T) { require := require.New(t) _, err := PatchBytes([]byte("foo"), nil) - require.EqualError(err, "error reading destination data: proto: cannot parse invalid wire-format data") + require.ErrorIs(err, proto.Error) + require.True(strings.HasPrefix(err.Error(), "error reading destination data: ")) }) t.Run("src", func(t *testing.T) { require := require.New(t) _, err := PatchBytes(nil, []byte("foo")) - require.EqualError(err, "error reading source data: proto: cannot parse invalid wire-format data") + require.ErrorIs(err, proto.Error) + require.True(strings.HasPrefix(err.Error(), "error reading source data: ")) }) } diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index 59a3bb1b5..a301b1368 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -326,7 +326,7 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal if authResults.Error != nil { return nil, authResults.Error } - hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + hc, plg, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req) if err != nil { return nil, err } @@ -338,6 +338,9 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal outputOpts := make([]handlers.Option, 0, 3) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } @@ -510,8 +513,8 @@ func (s Service) createInRepo(ctx context.Context, projId string, req *pbs.Creat return hc, plg, err } -func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*static.HostCatalog, error) { - const op = "host_catalogs.(Service).updateInRepo" +func (s Service) updateStaticInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*static.HostCatalog, error) { + const op = "host_catalogs.(Service).updateStaticInRepo" h, err := toStorageStaticCatalog(ctx, projId, item) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build host catalog for update")) @@ -536,6 +539,44 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str return out, nil } +func (s Service) updatePluginInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*plugin.HostCatalog, *plugins.PluginInfo, error) { + const op = "host_catalogs.(Service).updatePluginInRepo" + h, err := toStoragePluginCatalog(ctx, projId, "", item) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build host catalog for update")) + } + version := item.GetVersion() + h.PublicId = id + dbMask := maskManager.Translate(mask, "attributes", "secrets") + if len(dbMask) == 0 { + return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) + } + + repo, err := s.pluginHostRepoFn() + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + out, plg, rowsUpdated, err := repo.UpdateCatalog(ctx, h, version, dbMask) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update host catalog")) + } + if rowsUpdated == 0 { + return nil, nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist or incorrect version provided.", id) + } + return out, toPluginInfo(plg), nil +} + +func (s Service) updateInRepo(ctx context.Context, projId string, req *pbs.UpdateHostCatalogRequest) (hc host.Catalog, plg *plugins.PluginInfo, err error) { + const op = "host_catalogs.(Service).updateInRepo" + switch host.SubtypeFromId(req.GetId()) { + case static.Subtype: + hc, err = s.updateStaticInRepo(ctx, projId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + case plugin.Subtype: + hc, plg, err = s.updatePluginInRepo(ctx, projId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + } + return +} + func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { const op = "host_catalogs.(Service).deleteFromRepo" rows := 0 @@ -793,9 +834,19 @@ func validateUpdateRequest(req *pbs.UpdateHostCatalogRequest) error { if req.GetItem().GetType() != "" && host.SubtypeFromType(req.GetItem().GetType()) != static.Subtype { badFields[globals.TypeField] = "Cannot modify resource type." } + if req.GetItem().GetPlugin() != nil { + badFields[globals.PluginField] = "This field is unused for this type of host catalog." + } + case plugin.Subtype: + if req.GetItem().GetType() != "" && host.SubtypeFromType(req.GetItem().GetType()) != plugin.Subtype { + badFields[globals.TypeField] = "Cannot modify resource type." + } + if req.GetItem().GetPlugin() != nil { + badFields[globals.PluginField] = "This is a read only field." + } } return badFields - }, static.HostCatalogPrefix) + }, static.HostCatalogPrefix, plugin.HostCatalogPrefix) } func validateDeleteRequest(req *pbs.DeleteHostCatalogRequest) error { diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 9e8193ed2..dde3262e0 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -33,6 +33,7 @@ import ( "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -972,7 +973,7 @@ func TestCreate_Plugin(t *testing.T) { } } -func TestUpdate(t *testing.T) { +func TestUpdate_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -1339,3 +1340,282 @@ func TestUpdate(t *testing.T) { }) } } + +func TestUpdate_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + + name := "test" + plg := host.TestPlugin(t, conn, name) + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + } + + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, plgm) + } + pluginRepo := func() (*host.Repository, error) { + return host.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + tested, err := host_catalogs.NewService(repoFn, pluginHostRepo, pluginRepo, iamRepoFn) + require.NoError(t, err, "Failed to create a new host catalog service.") + + ctx := auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()) + + freshCatalog := func(t *testing.T) *pb.HostCatalog { + attr, err := structpb.NewStruct(map[string]interface{}{ + "foo": "bar", + }) + require.NoError(t, err) + resp, err := tested.CreateHostCatalog(ctx, &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + ScopeId: proj.GetPublicId(), + PluginId: plg.GetPublicId(), + Name: wrapperspb.String("default"), + Description: wrapperspb.String("default"), + Type: "plugin", + Attributes: attr, + }}) + require.NoError(t, err) + id := resp.GetItem().GetId() + t.Cleanup(func() { + _, err := tested.DeleteHostCatalog(ctx, &pbs.DeleteHostCatalogRequest{Id: id}) + require.NoError(t, err) + }) + return resp.GetItem() + } + + type updateFn func(catalog *pb.HostCatalog) + clearReadOnlyFields := func() updateFn { + return func(c *pb.HostCatalog) { + c.Id = "" + c.Plugin = nil + c.AuthorizedActions = nil + c.AuthorizedCollectionActions = nil + c.CreatedTime = nil + c.UpdatedTime = nil + } + } + updateName := func(i *wrappers.StringValue) updateFn { + return func(c *pb.HostCatalog) { + c.Name = i + } + } + updateDesc := func(i *wrappers.StringValue) updateFn { + return func(c *pb.HostCatalog) { + c.Description = i + } + } + updateAttrs := func(i *structpb.Struct) updateFn { + return func(c *pb.HostCatalog) { + c.Attributes = i + } + } + + cases := []struct { + name string + masks []string + changes []updateFn + check func(*testing.T, *pb.HostCatalog) + err error + }{ + { + name: "Update an Existing HostCatalog", + masks: []string{"name", "description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Equal(t, "new", in.Name.GetValue()) + assert.Equal(t, "desc", in.Description.GetValue()) + }, + }, + { + name: "Update arbitrary attribute for catalog", + masks: []string{"attributes.newkey", "attributes.foo"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateAttrs(func() *structpb.Struct { + attr, err := structpb.NewStruct(map[string]interface{}{ + "newkey": "newvalue", + "foo": nil, + }) + require.NoError(t, err) + return attr + }()), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Equal(t, map[string]interface{}{"newkey": "newvalue"}, in.GetAttributes().AsMap()) + }, + }, + { + name: "Multiple Paths in single string", + masks: []string{"name,description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Equal(t, "new", in.Name.GetValue()) + assert.Equal(t, "desc", in.Description.GetValue()) + }, + }, + { + name: "No Update Mask", + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Empty Path", + masks: []string{}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Only non-existant paths in Mask", + masks: []string{"nonexistant_field"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Unset Name", + masks: []string{"name"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(nil), + updateDesc(wrapperspb.String("ignored")), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Nil(t, in.Name) + assert.Equal(t, "default", in.Description.GetValue()) + }, + }, + { + name: "Unset Description", + masks: []string{"description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("ignored")), + updateDesc(nil), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Nil(t, in.Description) + assert.Equal(t, "default", in.Name.GetValue()) + }, + }, + { + name: "Update Only Name", + masks: []string{"name"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("updated")), + updateDesc(wrapperspb.String("ignored")), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Equal(t, "updated", in.Name.GetValue()) + assert.Equal(t, "default", in.Description.GetValue()) + }, + }, + { + name: "Update Only Description", + masks: []string{"description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("ignored")), + updateDesc(wrapperspb.String("updated")), + }, + check: func(t *testing.T, in *pb.HostCatalog) { + assert.Equal(t, "default", in.Name.GetValue()) + assert.Equal(t, "updated", in.Description.GetValue()) + }, + }, + { + name: "Cant change type", + masks: []string{"type"}, + changes: []updateFn{ + clearReadOnlyFields(), + func(c *pb.HostCatalog) { + c.Type = "static" + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify Updated Time", + masks: []string{"updated_time"}, + changes: []updateFn{ + clearReadOnlyFields(), + func(c *pb.HostCatalog) { + c.UpdatedTime = timestamppb.Now() + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + hc := freshCatalog(t) + + id := hc.GetId() + for _, f := range tc.changes { + f(hc) + } + + req := &pbs.UpdateHostCatalogRequest{ + Id: id, + Item: hc, + UpdateMask: &field_mask.FieldMask{Paths: tc.masks}, + } + + // Test some bad versions + req.Item.Version = 2 + _, gErr := tested.UpdateHostCatalog(ctx, req) + require.Error(gErr) + req.Item.Version = 0 + _, gErr = tested.UpdateHostCatalog(ctx, req) + require.Error(gErr) + req.Item.Version = 1 + + got, gErr := tested.UpdateHostCatalog(ctx, req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "UpdateHostCatalog(%+v) got error %v, wanted %v", req, gErr, tc.err) + return + } + require.NoError(gErr) + require.NotNil(got) + + item := got.GetItem() + assert.Equal(uint32(2), item.GetVersion()) + assert.Greater(item.GetUpdatedTime().AsTime().UnixNano(), item.GetCreatedTime().AsTime().UnixNano()) + tc.check(t, item) + }) + } +} diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 4d44777e5..1bd93bf6e 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -37,7 +37,7 @@ var ( // IdActions contains the set of actions that can be performed on // individual resources idActionsTypeMap = map[subtypes.Subtype]action.ActionSet{ - static.Subtype: action.ActionSet{ + static.Subtype: { action.NoOp, action.Read, action.Update, @@ -46,7 +46,7 @@ var ( action.SetHosts, action.RemoveHosts, }, - plugin.Subtype: action.ActionSet{ + plugin.Subtype: { action.NoOp, action.Read, action.Update, diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 7fac8c530..1122ee407 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -815,7 +815,7 @@ func TestCreate_Plugin(t *testing.T) { } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - testAttrs, err := structpb.NewStruct(map[string]interface{}{ + attrs := map[string]interface{}{ "int": 1, "zero int": 0, "string": "foo", @@ -834,7 +834,13 @@ func TestCreate_Plugin(t *testing.T) { "bool": true, "zero bool": false, }, - }) + } + testInputAttrs, err := structpb.NewStruct(attrs) + require.NoError(t, err) + // The result should clear out all keys with nil values... + delete(attrs, "zero bytes") + delete(attrs["nested"].(map[string]interface{}), "zero bytes") + testOutputAttrs, err := structpb.NewStruct(attrs) require.NoError(t, err) prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} @@ -879,7 +885,7 @@ func TestCreate_Plugin(t *testing.T) { Name: &wrappers.StringValue{Value: "With Attributes"}, Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), - Attributes: testAttrs, + Attributes: testInputAttrs, }}, res: &pbs.CreateHostSetResponse{ Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), @@ -895,7 +901,7 @@ func TestCreate_Plugin(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, Type: plugin.Subtype.String(), AuthorizedActions: testAuthorizedActions[plugin.Subtype], - Attributes: testAttrs, + Attributes: testOutputAttrs, }, }, }, diff --git a/internal/tests/api/hostcatalogs/host_catalog_test.go b/internal/tests/api/hostcatalogs/host_catalog_test.go index 5e60d3eeb..d7c74c39d 100644 --- a/internal/tests/api/hostcatalogs/host_catalog_test.go +++ b/internal/tests/api/hostcatalogs/host_catalog_test.go @@ -169,6 +169,17 @@ func TestCrud(t *testing.T) { hostcatalogs.WithAttributes(map[string]interface{}{"foo": "bar"})) require.NoError(err) + c, err = hcClient.Update(tc.Context(), c.Item.Id, c.Item.Version, hostcatalogs.WithName("bar"), + hostcatalogs.WithAttributes(map[string]interface{}{"key": "val", "foo": nil}), + hostcatalogs.WithSecrets(map[string]interface{}{"secretkey": "secretval"})) + checkCatalog("update", c.Item, err, "bar", 2) + assert.Contains(c.Item.Attributes, "key") + assert.NotContains(c.Item.Attributes, "foo") + assert.Empty(c.Item.Secrets) + + c, err = hcClient.Update(tc.Context(), c.Item.Id, c.Item.Version, hostcatalogs.DefaultName()) + checkCatalog("update", c.Item, err, "", 3) + c, err = hcClient.Read(tc.Context(), c.Item.Id) assert.NoError(err) diff --git a/internal/tests/api/hosts/host_test.go b/internal/tests/api/hosts/host_test.go index 0c4391758..688fdee10 100644 --- a/internal/tests/api/hosts/host_test.go +++ b/internal/tests/api/hosts/host_test.go @@ -99,20 +99,20 @@ func TestPluginHosts(t *testing.T) { hset, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id, hostsets.WithAttributes(map[string]interface{}{ "host_info": []interface{}{ map[string]interface{}{ - "external_id": "test1", + "external_id": "test1", "ip_addresses": []string{"10.0.0.1", "192.168.1.1"}, - "dns_names": []string{"foo.hashicorp.com", "boundaryproject.io"}, + "dns_names": []string{"foo.hashicorp.com", "boundaryproject.io"}, }, map[string]interface{}{ - "external_id": "test2", + "external_id": "test2", "ip_addresses": []string{"10.0.0.2", "192.168.1.2"}, - "dns_names": []string{"foo2.hashicorp.com", "boundaryproject2.io"}, + "dns_names": []string{"foo2.hashicorp.com", "boundaryproject2.io"}, }, }, })) require.NoError(err) require.NotNil(hset) - time.Sleep(1*time.Second) + time.Sleep(1 * time.Second) hClient := hosts.NewClient(client) hl, err := hClient.List(tc.Context(), hc.Item.Id) From 2229d1a819a6f4fbe524b6a9356ddb6b8aeea081 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 10 Nov 2021 09:04:29 -0700 Subject: [PATCH 55/71] Call OnCreate or OnUpdate on the plugin after db constraints are checked (#1683) * Only call the Plugin at create/update time after the db has been partially updated. This allows more constraints to be checked prior to calling the plugin. --- .../host/plugin/repository_host_catalog.go | 184 +++++++++--------- .../plugin/repository_host_catalog_test.go | 76 ++++++-- internal/host/plugin/repository_host_set.go | 16 +- .../host/plugin/repository_host_set_test.go | 58 +++++- 4 files changed, 214 insertions(+), 120 deletions(-) diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index c99e433be..42afeb81c 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -76,33 +76,17 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt if !ok || plgClient == nil { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("plugin %q not available", c.GetPluginId())) } - plgResp, err := plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc}) - if err != nil { - if status.Code(err) != codes.Unimplemented { - return nil, nil, errors.Wrap(ctx, err, op) - } - } - - var hcSecret *HostCatalogSecret - if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { - hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetSecrets()) - if err != nil { - return nil, nil, errors.Wrap(ctx, err, op) - } - dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) - if err != nil { - return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) - } - if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { - return nil, nil, errors.Wrap(ctx, err, op) - } - } oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog) if err != nil { return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } + // If the call to the plugin succeeded, we do not want to call it again if + // the transaction failed and is being retried. + var pluginCalledSuccessfully bool + var plgResp *plgpb.OnCreateCatalogResponse + var newHostCatalog *HostCatalog _, err = r.writer.DoTx( ctx, @@ -122,17 +106,40 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt } msgs = append(msgs, &cOplogMsg) - if hcSecret != nil { - newSecret := hcSecret.clone() - q, v := newSecret.upsertQuery() - rows, err := w.Exec(ctx, q, v) + if !pluginCalledSuccessfully { + plgResp, err = plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc}) + if err != nil { + if status.Code(err) != codes.Unimplemented { + return errors.Wrap(ctx, err, op) + } + } + pluginCalledSuccessfully = true + } + + if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { + hcSecret, err := newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetSecrets()) if err != nil { return errors.Wrap(ctx, err, op) } - if rows > 1 { - return errors.New(ctx, errors.MultipleRecords, op, "more than 1 catalog secret would have been created") + dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) + } + if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { + return errors.Wrap(ctx, err, op) + } + if hcSecret != nil { + newSecret := hcSecret.clone() + q, v := newSecret.upsertQuery() + rows, err := w.Exec(ctx, q, v) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rows > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 catalog secret would have been created") + } + msgs = append(msgs, newSecret.oplogMessage(db.CreateOp)) } - msgs = append(msgs, newSecret.oplogMessage(db.CreateOp)) } metadata := c.oplog(oplog.OpType_OP_TYPE_CREATE) @@ -284,51 +291,19 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) } - plgResp, err := plgClient.OnUpdateCatalog(ctx, &plgpb.OnUpdateCatalogRequest{ - CurrentCatalog: currPlgHc, - NewCatalog: newPlgHc, - Persisted: currentCatalogPersisted, - }) + dbWrapper, err := r.kms.GetWrapper(ctx, newCatalog.ScopeId, kms.KeyPurposeDatabase) if err != nil { - if status.Code(err) != codes.Unimplemented { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) - } + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) } - - // Success for OnUpdateCatalog. This means we can start updating. - // First, check for returned persisted data and encrypt it. - var hcSecret *HostCatalogSecret - var deleteSecrets bool - if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { - if len(plgResp.GetPersisted().GetSecrets().GetFields()) == 0 { - // Flag the secret to be deleted. - hcSecret, err = newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) - if err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) - } - - deleteSecrets = true - } else { - hcSecret, err = newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) - if err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) - } - dbWrapper, err := r.kms.GetWrapper(ctx, newCatalog.ScopeId, kms.KeyPurposeDatabase) - if err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper")) - } - if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) - } - } - } - // Get the oplog. oplogWrapper, err := r.kms.GetWrapper(ctx, newCatalog.ScopeId, kms.KeyPurposeOplog) if err != nil { return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } + var pluginCalledSuccessfully bool + var plgResp *plgpb.OnUpdateCatalogResponse + var recordUpdated bool var returnedCatalog *HostCatalog _, err = r.writer.DoTx( @@ -368,13 +343,31 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version returnedCatalog = currentCatalog.clone() } - if hcSecret != nil { - if deleteSecrets { + if !pluginCalledSuccessfully { + plgResp, err = plgClient.OnUpdateCatalog(ctx, &plgpb.OnUpdateCatalogRequest{ + CurrentCatalog: currPlgHc, + NewCatalog: newPlgHc, + Persisted: currentCatalogPersisted, + }) + if err != nil { + if status.Code(err) != codes.Unimplemented { + return errors.Wrap(ctx, err, op) + } + } + pluginCalledSuccessfully = true + } + var updatedPersisted bool + if plgResp != nil && plgResp.GetPersisted().GetSecrets() != nil { + if len(plgResp.GetPersisted().GetSecrets().GetFields()) == 0 { + // Flag the secret to be deleted. + hcSecret, err := newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) + if err != nil { + return errors.Wrap(ctx, err, op) + } // We didn't set/encrypt the persisted data because there was // none returned. Just delete the entry. deletedSecret := hcSecret.clone() q, v := deletedSecret.deleteQuery() - var err error secretsUpdated, err := w.Exec(ctx, q, v) if err != nil { return errors.Wrap(ctx, err, op) @@ -382,12 +375,20 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version if secretsUpdated != 1 { return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog secret to be deleted, got %d", secretsUpdated)) } + updatedPersisted = true msgs = append(msgs, deletedSecret.oplogMessage(db.DeleteOp)) } else { + hcSecret, err := newHostCatalogSecret(ctx, currentCatalog.GetPublicId(), plgResp.GetPersisted().GetSecrets()) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if err := hcSecret.encrypt(ctx, dbWrapper); err != nil { + return errors.Wrap(ctx, err, op) + } + // Update the secrets. updatedSecret := hcSecret.clone() q, v := updatedSecret.upsertQuery() - var err error secretsUpdated, err := w.Exec(ctx, q, v) if err != nil { return errors.Wrap(ctx, err, op) @@ -395,32 +396,33 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version if secretsUpdated != 1 { return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog secret to be updated, got %d", secretsUpdated)) } + updatedPersisted = true msgs = append(msgs, updatedSecret.oplogMessage(db.UpdateOp)) } + } - if !recordUpdated { - // we only updated secrets, so we need to increment the - // version of the host catalog manually. - returnedCatalog = newCatalog.clone() - returnedCatalog.Version = uint32(version) + 1 - var cOplogMsg oplog.Message - catalogsUpdated, err := w.Update( - ctx, - returnedCatalog, - []string{"version"}, - []string{}, - db.NewOplogMsg(&cOplogMsg), - db.WithVersion(&version), - ) - if err != nil { - return errors.Wrap(ctx, err, op) - } - if catalogsUpdated != 1 { - return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog to be updated, got %d", catalogsUpdated)) - } - msgs = append(msgs, &cOplogMsg) - recordUpdated = true + if !recordUpdated && updatedPersisted { + // we only updated secrets, so we need to increment the + // version of the host catalog manually. + returnedCatalog = newCatalog.clone() + returnedCatalog.Version = uint32(version) + 1 + var cOplogMsg oplog.Message + catalogsUpdated, err := w.Update( + ctx, + returnedCatalog, + []string{"version"}, + []string{}, + db.NewOplogMsg(&cOplogMsg), + db.WithVersion(&version), + ) + if err != nil { + return errors.Wrap(ctx, err, op) } + if catalogsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 catalog to be updated, got %d", catalogsUpdated)) + } + msgs = append(msgs, &cOplogMsg) + recordUpdated = true } if len(msgs) != 0 { diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index bf9e0c0d2..ac69d403d 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -36,26 +36,12 @@ func TestRepository_CreateCatalog(t *testing.T) { plg := hostplg.TestPlugin(t, conn, "test") unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") - // gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the - // test against the expected values sent to the plugin. - var gotPluginAttrs *structpb.Struct - plgm := map[string]plgpb.HostPluginServiceClient{ - plg.GetPublicId(): &WrappingPluginClient{ - Server: &TestPluginServer{ - OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { - gotPluginAttrs = req.GetCatalog().GetAttributes() - return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil - }, - }, - }, - unimplementedPlugin.GetPublicId(): &WrappingPluginClient{Server: &plgpb.UnimplementedHostPluginServiceServer{}}, - } - tests := []struct { name string in *HostCatalog opts []Option want *HostCatalog + wantPluginCalled bool wantSecret *structpb.Struct wantIsErr errors.Code }{ @@ -114,6 +100,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Attributes: []byte{}, }, }, + wantPluginCalled: true, }, { name: "valid-unimplemented-plugin", @@ -131,6 +118,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Attributes: []byte{}, }, }, + wantPluginCalled: true, }, { name: "not-found-plugin", @@ -161,6 +149,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Attributes: []byte{}, }, }, + wantPluginCalled: true, }, { name: "valid-with-description", @@ -180,6 +169,7 @@ func TestRepository_CreateCatalog(t *testing.T) { Attributes: []byte{}, }, }, + wantPluginCalled: true, }, { name: "valid-with-attributes", @@ -212,6 +202,7 @@ func TestRepository_CreateCatalog(t *testing.T) { }(), }, }, + wantPluginCalled: true, }, { name: "valid-with-secret", @@ -249,6 +240,7 @@ func TestRepository_CreateCatalog(t *testing.T) { require.NoError(t, err) return st }(), + wantPluginCalled: true, }, } @@ -257,10 +249,34 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) kmsCache := kms.TestKms(t, conn, wrapper) + + // gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the + // test against the expected values sent to the plugin. + var gotPluginAttrs *structpb.Struct + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + pluginCalled = true + gotPluginAttrs = req.GetCatalog().GetAttributes() + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil + }, + }, + }, + unimplementedPlugin.GetPublicId(): &WrappingPluginClient{Server: &TestPluginServer{ + OnCreateCatalogFn: func(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + pluginCalled = true + gotPluginAttrs = req.GetCatalog().GetAttributes() + return plgpb.UnimplementedHostPluginServiceServer{}.OnCreateCatalog(ctx, req) + }, + }}, + } repo, err := NewRepository(rw, rw, kmsCache, sched, plgm) assert.NoError(err) assert.NotNil(repo) got, _, err := repo.CreateCatalog(ctx, tt.in, tt.opts...) + assert.Equal(tt.wantPluginCalled, pluginCalled) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -308,6 +324,17 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run("invalid-duplicate-names", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + pluginCalled = true + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil + }, + }, + }, + } repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) @@ -324,13 +351,18 @@ func TestRepository_CreateCatalog(t *testing.T) { got, _, err := repo.CreateCatalog(context.Background(), in) assert.NoError(err) assert.NotNil(got) + assert.True(pluginCalled) assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) assert.NotSame(in, got) assert.Equal(in.Name, got.Name) assert.Equal(in.Description, got.Description) assert.Equal(got.CreateTime, got.UpdateTime) + // Reset pluginCalled so we can see that the plugin wasn't called w/ + // the duplicate name. + pluginCalled = false got2, _, err := repo.CreateCatalog(context.Background(), in) + assert.False(pluginCalled) assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) assert.Nil(got2) }) @@ -338,6 +370,17 @@ func TestRepository_CreateCatalog(t *testing.T) { t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) { assert := assert.New(t) kms := kms.TestKms(t, conn, wrapper) + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) { + pluginCalled = true + return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Secrets: req.GetCatalog().GetSecrets()}}, nil + }, + }, + }, + } repo, err := NewRepository(rw, rw, kms, sched, plgm) assert.NoError(err) assert.NotNil(repo) @@ -355,6 +398,7 @@ func TestRepository_CreateCatalog(t *testing.T) { got, _, err := repo.CreateCatalog(context.Background(), in) assert.NoError(err) assert.NotNil(got) + assert.True(pluginCalled) assertPluginBasedPublicId(t, HostCatalogPrefix, got.PublicId) assert.NotSame(in, got) assert.Equal(in.Name, got.Name) @@ -362,9 +406,11 @@ func TestRepository_CreateCatalog(t *testing.T) { assert.Equal(got.CreateTime, got.UpdateTime) in2.ScopeId = org.GetPublicId() + pluginCalled = false // reset pluginCalled for this next call to Create got2, _, err := repo.CreateCatalog(context.Background(), in2) assert.NoError(err) assert.NotNil(got2) + assert.True(pluginCalled) assertPluginBasedPublicId(t, HostCatalogPrefix, got2.PublicId) assert.NotSame(in2, got2) assert.Equal(in2.Name, got2.Name) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 958b557f1..5997ab363 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -85,11 +85,6 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, if err != nil { return nil, nil, errors.Wrap(ctx, err, op) } - if _, err := plgClient.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{Catalog: plgHc, Set: plgHs, Persisted: per}); err != nil { - if status.Code(err) != codes.Unimplemented { - return nil, nil, errors.Wrap(ctx, err, op) - } - } var preferredEndpoints []interface{} if s.PreferredEndpoints != nil { @@ -108,6 +103,8 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } + var calledPluginSuccessfully bool + var returnedHostSet *HostSet _, err = r.writer.DoTx( ctx, @@ -136,6 +133,15 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, msgs = append(msgs, peOplogMsgs...) } + if !calledPluginSuccessfully { + if _, err := plgClient.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{Catalog: plgHc, Set: plgHs, Persisted: per}); err != nil { + if status.Code(err) != codes.Unimplemented { + return errors.Wrap(ctx, err, op) + } + } + calledPluginSuccessfully = true + } + metadata := s.oplog(oplog.OpType_OP_TYPE_CREATE) if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index cc36690d1..017dad03e 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -38,15 +38,6 @@ func TestRepository_CreateSet(t *testing.T) { plg := hostplg.TestPlugin(t, conn, "create") unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") - var pluginReceivedAttrs *structpb.Struct - plgm := map[string]plgpb.HostPluginServiceClient{ - plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { - pluginReceivedAttrs = req.GetSet().GetAttributes() - return &plgpb.OnCreateSetResponse{}, nil - }}), - unimplementedPlugin.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}), - } - catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) unimplementedPluginCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) attrs := []byte{} @@ -56,6 +47,7 @@ func TestRepository_CreateSet(t *testing.T) { in *HostSet opts []Option want *HostSet + wantPluginCalled bool wantIsErr errors.Code }{ { @@ -110,6 +102,7 @@ func TestRepository_CreateSet(t *testing.T) { Attributes: attrs, }, }, + wantPluginCalled: true, }, { name: "valid-preferred-endpoints", @@ -127,6 +120,7 @@ func TestRepository_CreateSet(t *testing.T) { PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, }, }, + wantPluginCalled: true, }, { name: "valid-with-name", @@ -144,6 +138,7 @@ func TestRepository_CreateSet(t *testing.T) { Attributes: attrs, }, }, + wantPluginCalled: true, }, { name: "valid-unimplemented-plugin", @@ -161,6 +156,7 @@ func TestRepository_CreateSet(t *testing.T) { Attributes: attrs, }, }, + wantPluginCalled: true, }, { name: "valid-with-description", @@ -178,6 +174,7 @@ func TestRepository_CreateSet(t *testing.T) { Attributes: attrs, }, }, + wantPluginCalled: true, }, { name: "valid-with-custom-attributes", @@ -208,6 +205,7 @@ func TestRepository_CreateSet(t *testing.T) { }(), }, }, + wantPluginCalled: true, }, } @@ -215,10 +213,25 @@ func TestRepository_CreateSet(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) + var pluginReceivedAttrs *structpb.Struct + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + pluginCalled = true + pluginReceivedAttrs = req.GetSet().GetAttributes() + return &plgpb.OnCreateSetResponse{}, nil + }}), + unimplementedPlugin.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + pluginCalled = true + pluginReceivedAttrs = req.GetSet().GetAttributes() + return plgpb.UnimplementedHostPluginServiceServer{}.OnCreateSet(ctx, req) + }}), + } repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) got, plgInfo, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...) + assert.Equal(tt.wantPluginCalled, pluginCalled) if tt.wantIsErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) assert.Nil(got) @@ -243,6 +256,13 @@ func TestRepository_CreateSet(t *testing.T) { t.Run("invalid-duplicate-names", func(t *testing.T) { assert, require := assert.New(t), require.New(t) + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + pluginCalled = true + return &plgpb.OnCreateSetResponse{}, nil + }}), + } repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) @@ -261,19 +281,31 @@ func TestRepository_CreateSet(t *testing.T) { got, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) require.NoError(err) require.NotNil(got) + assert.True(pluginCalled) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) assert.NotSame(in, got) assert.Equal(in.Name, got.GetName()) assert.Equal(in.Description, got.GetDescription()) assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) + // reset pluginCalled so we can see the duplicate name causes the plugin + // to not get called. + pluginCalled = false got2, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + assert.False(pluginCalled) assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) assert.Nil(got2) }) t.Run("valid-duplicate-names-diff-catalogs", func(t *testing.T) { assert, require := assert.New(t), require.New(t) + var pluginCalled bool + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): NewWrappingPluginClient(TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { + pluginCalled = true + return &plgpb.OnCreateSetResponse{}, nil + }}), + } repo, err := NewRepository(rw, rw, kms, sched, plgm) require.NoError(err) require.NotNil(repo) @@ -291,7 +323,11 @@ func TestRepository_CreateSet(t *testing.T) { in2 := in.clone() in.CatalogId = catalogA.PublicId + // reset pluginCalled so we can see that the plugin is called during this + // createSet call + pluginCalled = false got, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in) + assert.True(pluginCalled) require.NoError(err) require.NotNil(got) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) @@ -301,9 +337,13 @@ func TestRepository_CreateSet(t *testing.T) { assert.Equal(got.GetCreateTime(), got.GetUpdateTime()) in2.CatalogId = catalogB.PublicId + // reset pluginCalled so we can see that the plugin is called during this + // createSet call + pluginCalled = false got2, _, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in2) require.NoError(err) require.NotNil(got2) + assert.True(pluginCalled) assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix)) assert.NotSame(in2, got2) assert.Equal(in2.Name, got2.GetName()) From 0ea3ff03c439ef0623aef92f9d439ade3f814474 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 10 Nov 2021 15:09:55 -0500 Subject: [PATCH 56/71] Update SDK go.mod to require Go 1.17 --- README.md | 2 +- sdk/go.mod | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------ sdk/go.sum | 1 - 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f2685151e..d65617018 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ for permanent installations, are detailed in our [Installation Guide](https://ww ### Build and Start Boundary in Dev Mode If you have the following requirements met locally: -- Golang v1.16 or greater +- Golang v1.17 or greater - Docker - [Boundary UI Dependencies](https://github.com/hashicorp/boundary-ui#prerequisites) diff --git a/sdk/go.mod b/sdk/go.mod index f84eb0c8b..18ac95c44 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -1,39 +1,106 @@ module github.com/hashicorp/boundary/sdk -go 1.15 +go 1.17 require ( - github.com/fatih/color v1.12.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-kms-wrapping v0.6.6 github.com/hashicorp/go-plugin v1.0.1 - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 + github.com/stretchr/testify v1.7.0 + google.golang.org/grpc v1.40.0 + google.golang.org/protobuf v1.27.1 +) + +require ( + cloud.google.com/go v0.65.0 // indirect + github.com/Azure/azure-sdk-for-go v36.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.10.1 // indirect + github.com/Azure/go-autorest/autorest/adal v0.8.2 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 // indirect + github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect + github.com/Azure/go-autorest/logger v0.1.0 // indirect + github.com/Azure/go-autorest/tracing v0.5.0 // indirect + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go v1.30.27 // indirect + github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/dimchansky/utfbom v1.1.0 // indirect + github.com/fatih/color v1.12.0 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.2 // indirect + github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/api v1.1.1 // indirect + github.com/hashicorp/vault/sdk v0.2.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect + github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/json-iterator/go v1.1.10 // indirect github.com/kr/pretty v0.2.1 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mitchellh/cli v1.1.2 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/oracle/oci-go-sdk v12.5.0+incompatible // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/smartystreets/assertions v1.0.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/objx v0.2.0 // indirect - github.com/stretchr/testify v1.7.0 go.opencensus.io v0.22.6 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect golang.org/x/tools v0.1.3 // indirect + google.golang.org/api v0.30.0 // indirect + google.golang.org/appengine v1.6.6 // indirect google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 // indirect - google.golang.org/grpc v1.40.0 - google.golang.org/protobuf v1.27.1 gopkg.in/ini.v1 v1.51.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/sdk/go.sum b/sdk/go.sum index fee39e1f1..6fbf66894 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -534,7 +534,6 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 3f3f6d974cdbdd38709dd7bb6c52dcb422753533 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 10 Nov 2021 15:10:18 -0500 Subject: [PATCH 57/71] Update API go.mod to require Go 1.17 --- api/go.mod | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/api/go.mod b/api/go.mod index 83b9febd2..136a7e302 100644 --- a/api/go.mod +++ b/api/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/boundary/api -go 1.15 +go 1.17 require ( github.com/fatih/structs v1.1.0 @@ -12,8 +12,25 @@ require ( github.com/hashicorp/go-uuid v1.0.2 github.com/mr-tron/base58 v1.2.0 github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.27.1 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.7.0 // indirect + github.com/golang/protobuf v1.5.0 // indirect + github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect + golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) From 962c9463fdb38540e163c59a643c9b313c083b19 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 10 Nov 2021 15:11:57 -0500 Subject: [PATCH 58/71] Update SDK/API modules --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 36854271d..ba85d8c21 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/google/go-cmp v0.5.6 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 - github.com/hashicorp/boundary/api v0.0.19 - github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 + github.com/hashicorp/boundary/api v0.0.20-0.20211110201018-3f3f6d974cdb + github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201018-3f3f6d974cdb github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 From 5c09db5e4a23966f0195b0099bb91d48554f708b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 10 Nov 2021 15:18:45 -0500 Subject: [PATCH 59/71] Update plugin --- plugins/host/mains/aws/go.mod | 44 +++++++++++++++++++++++++++++++---- plugins/host/mains/aws/go.sum | 22 ++++++++++-------- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod index 5c590fda2..e910c71be 100644 --- a/plugins/host/mains/aws/go.mod +++ b/plugins/host/mains/aws/go.mod @@ -1,6 +1,12 @@ module github.com/hashicorp/boundary/plugins/host/mains/aws -go 1.16 +go 1.17 + +require ( + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 + github.com/hashicorp/go-hclog v1.0.0 +) require ( github.com/Azure/azure-sdk-for-go v57.4.0+incompatible // indirect @@ -8,10 +14,40 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e - github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 - github.com/hashicorp/go-hclog v0.16.2 + github.com/aws/aws-sdk-go v1.42.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 // indirect + github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-kms-wrapping v0.6.6 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.7.0 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect + golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef // indirect + golang.org/x/text v0.3.6 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect + google.golang.org/grpc v1.40.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum index 2ceb12714..df8f0a197 100644 --- a/plugins/host/mains/aws/go.sum +++ b/plugins/host/mains/aws/go.sum @@ -108,8 +108,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.41.16 h1:rNbPreEO4K2b8LwhHOe5+iNRH3Ih35L+H9U5tLdcevo= -github.com/aws/aws-sdk-go v1.41.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.42.2 h1:SXA+B3DT4N3+wJw5X4Jz9/PazkQZQ7k1nXLGZRdFbO4= +github.com/aws/aws-sdk-go v1.42.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -257,17 +257,18 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e h1:G4C/kxlAnJdas2dUfLKiQlR7MHIhia+zr+Aghj1S52Q= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211104012127-1de471d9c71e/go.mod h1:p41IT5ux9TOshMDpS2s21HW2ShBgcqAn8kC4Ee9U5uQ= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0 h1:QGlibT6NxZap22A7JyleinwGzlW3HB9hKJuzErmUX0E= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0/go.mod h1:4tHcCu6L2jtMU7/b+K7QAG6R2d26wHBBuu+lPLVJRio= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 h1:hl/QIALmFBG8bewvH6s/iB4H1kAXOT3DYreX9JTULIs= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3/go.mod h1:gJoqOSNZfoclvLvBqAnfarha9YbZCtuRiYpu64GWX1Q= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -277,8 +278,9 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping v0.6.4/go.mod h1:pRK4UPSmNLEAjLE2qVz7nvS4NTZyyypQCn2G4q7vkps= From 3a5f0a2981bbe44029891b5de139b1aec7de0de8 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 10 Nov 2021 15:20:48 -0500 Subject: [PATCH 60/71] Update plugin --- plugins/host/mains/azure/go.mod | 61 ++++++++++++++++++++++++++++++--- plugins/host/mains/azure/go.sum | 48 ++++++++++++++------------ 2 files changed, 82 insertions(+), 27 deletions(-) diff --git a/plugins/host/mains/azure/go.mod b/plugins/host/mains/azure/go.mod index 7d77c36b3..c10c33e1a 100644 --- a/plugins/host/mains/azure/go.mod +++ b/plugins/host/mains/azure/go.mod @@ -1,10 +1,61 @@ module github.com/hashicorp/boundary/plugins/host/mains/azure -go 1.16 +go 1.17 require ( - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a - github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 - github.com/hashicorp/go-hclog v0.16.2 - github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6 + github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 + github.com/hashicorp/go-hclog v1.0.0 +) + +require ( + github.com/Azure/azure-sdk-for-go v59.2.0+incompatible // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.22 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.14 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.9 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/fatih/color v1.12.0 // indirect + github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 // indirect + github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-kms-wrapping v0.6.6 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.3.0 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/manicminer/hamilton v0.33.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.7.0 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect + golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 // indirect + google.golang.org/grpc v1.40.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/plugins/host/mains/azure/go.sum b/plugins/host/mains/azure/go.sum index 07571c337..8c3a529e3 100644 --- a/plugins/host/mains/azure/go.sum +++ b/plugins/host/mains/azure/go.sum @@ -34,28 +34,28 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM= -github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v59.2.0+incompatible h1:mbxiZy1K820hQ+dI+YIO/+a0wQDYqOu18BAGe4lXjVk= +github.com/Azure/azure-sdk-for-go v59.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= -github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= +github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.22 h1:bXiQwDjrRmBQOE67bwlvUKAC1EU1yZTPQ38c+bstZws= +github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.9 h1:Y2CgdzitFDsdMwYMzf9LIZWrrTFysqbRc7b94XVVJ78= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.9/go.mod h1:hg3/1yw0Bq87O3KvvnJoAh34/0zbP7SFizX/qN5JvjU= github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= @@ -75,7 +75,6 @@ github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRY github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= @@ -255,17 +254,18 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a h1:pEMK2MU4n+Xqp322Qd5agXq8FxiBs2PFD2s1OtnBH8o= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211029205634-22b62c202f6a/go.mod h1:SqjllApBoUV47+IzNfdnzr9jXnNjX8kx4yk2Csc7aCM= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54= -github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6 h1:A7WYNoRNzmcDsrnEU05O5r831VLfPC8W5tfPgAvnSiI= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6/go.mod h1:SmtW3WJ4MSQVmC4Fjs8tqI6EOrHw3LnwlTOvDhybcwk= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 h1:hl/QIALmFBG8bewvH6s/iB4H1kAXOT3DYreX9JTULIs= +github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3/go.mod h1:gJoqOSNZfoclvLvBqAnfarha9YbZCtuRiYpu64GWX1Q= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -275,8 +275,9 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping v0.6.4/go.mod h1:pRK4UPSmNLEAjLE2qVz7nvS4NTZyyypQCn2G4q7vkps= @@ -307,8 +308,9 @@ github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7 github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ= github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1 h1:HlTofNQN49DTzPCMVDppp+AVxMNVWaHUElpUYC0TRRE= github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1/go.mod h1:irE9ILwhE8VooWdhHhBy0qXzOMRlHKoWoMlr31mAFCE= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2 h1:Tz6v3Jb2DRnDCfifRSjYKG0m8dLdNq6bcDkB41en7nw= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI= github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= @@ -375,8 +377,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/manicminer/hamilton v0.32.0 h1:awVHVXOLp9P5QbWaOFqneuWjAvzAk3BYtTEX6AeWB5k= -github.com/manicminer/hamilton v0.32.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U= +github.com/manicminer/hamilton v0.33.0 h1:G58jBVHg2qsJqb5cPJ3tKzWj3JTf8gW3r5+4RmnEa0E= +github.com/manicminer/hamilton v0.33.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -403,8 +405,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -528,8 +531,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From b14302e762398403d71d6768526d7791a6f9a1a4 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 10 Nov 2021 15:30:40 -0700 Subject: [PATCH 61/71] Updating the azure plugin dependency. --- plugins/host/mains/azure/go.mod | 2 +- plugins/host/mains/azure/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/host/mains/azure/go.mod b/plugins/host/mains/azure/go.mod index c10c33e1a..e7af43ae7 100644 --- a/plugins/host/mains/azure/go.mod +++ b/plugins/host/mains/azure/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/boundary/plugins/host/mains/azure go 1.17 require ( - github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6 + github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110222619-3aff7f5aba17 github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 github.com/hashicorp/go-hclog v1.0.0 ) diff --git a/plugins/host/mains/azure/go.sum b/plugins/host/mains/azure/go.sum index 8c3a529e3..68aeec119 100644 --- a/plugins/host/mains/azure/go.sum +++ b/plugins/host/mains/azure/go.sum @@ -254,8 +254,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6 h1:A7WYNoRNzmcDsrnEU05O5r831VLfPC8W5tfPgAvnSiI= -github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110201326-7197294c2cf6/go.mod h1:SmtW3WJ4MSQVmC4Fjs8tqI6EOrHw3LnwlTOvDhybcwk= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110222619-3aff7f5aba17 h1:zyGfx7GgdSOtnqVQDHQ8Qi+sJTrHIE7rBsAefL7Sa6I= +github.com/hashicorp/boundary-plugin-host-azure v0.0.0-20211110222619-3aff7f5aba17/go.mod h1:SmtW3WJ4MSQVmC4Fjs8tqI6EOrHw3LnwlTOvDhybcwk= github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 h1:hl/QIALmFBG8bewvH6s/iB4H1kAXOT3DYreX9JTULIs= github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3/go.mod h1:gJoqOSNZfoclvLvBqAnfarha9YbZCtuRiYpu64GWX1Q= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 772bb88d8dcaf7f754eb7ec32a9060495afabc34 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 10 Nov 2021 15:42:51 -0700 Subject: [PATCH 62/71] Updating the aws plugin dependency. --- plugins/host/mains/aws/go.mod | 2 +- plugins/host/mains/aws/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/host/mains/aws/go.mod b/plugins/host/mains/aws/go.mod index e910c71be..2b14f5d14 100644 --- a/plugins/host/mains/aws/go.mod +++ b/plugins/host/mains/aws/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/boundary/plugins/host/mains/aws go 1.17 require ( - github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0 + github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110224123-563a2b99315c github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 github.com/hashicorp/go-hclog v1.0.0 ) diff --git a/plugins/host/mains/aws/go.sum b/plugins/host/mains/aws/go.sum index df8f0a197..8c2733eea 100644 --- a/plugins/host/mains/aws/go.sum +++ b/plugins/host/mains/aws/go.sum @@ -257,8 +257,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0 h1:QGlibT6NxZap22A7JyleinwGzlW3HB9hKJuzErmUX0E= -github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110201244-a0de5b0c55d0/go.mod h1:4tHcCu6L2jtMU7/b+K7QAG6R2d26wHBBuu+lPLVJRio= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110224123-563a2b99315c h1:PWYD0d5D6RNbBzI3iQfUsGSg8hJEH7qe0kfozPAZ7L4= +github.com/hashicorp/boundary-plugin-host-aws v0.0.0-20211110224123-563a2b99315c/go.mod h1:4tHcCu6L2jtMU7/b+K7QAG6R2d26wHBBuu+lPLVJRio= github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3 h1:hl/QIALmFBG8bewvH6s/iB4H1kAXOT3DYreX9JTULIs= github.com/hashicorp/boundary/sdk v0.0.12-0.20211110201157-962c9463fdb3/go.mod h1:gJoqOSNZfoclvLvBqAnfarha9YbZCtuRiYpu64GWX1Q= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From c2cd6febcaf382d3e7a22eb620380b5457e19fe0 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Wed, 10 Nov 2021 17:08:42 -0800 Subject: [PATCH 63/71] internal/host/plugin: add UpdateSet (#1686) * internal/host/plugin: add UpdateSet This adds the UpdateSet repository function. UpdateSet runs the OnUpdateSet plugin call within the transaction as per the changes in #1683. The plugin call returns the updated set, the hosts in the set, the rows updated, and any error. * Ensure that attributes can be cleared by supplying a full null * move version increment to the end and track perferred endpoint update separately * move plugin call to end of transaction * only clone returned set once, and move host fetch call out of transaction * Request a sync when attributes are updated * Add test to assert that old preferred endpoints are being removed correctly --- internal/host/plugin/repository_host_set.go | 355 ++++++++- .../host/plugin/repository_host_set_test.go | 754 ++++++++++++++++++ 2 files changed, 1108 insertions(+), 1 deletion(-) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 5997ab363..a4f2bfd94 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -3,12 +3,14 @@ package plugin import ( "context" "fmt" + "strings" "time" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" + "github.com/hashicorp/boundary/internal/host/store" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/libs/patchstruct" @@ -20,6 +22,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" ) // CreateSet inserts s into the repository and returns a new HostSet @@ -168,6 +171,344 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, return returnedHostSet, plg, nil } +// UpdateSet updates the repository for host set entry s with the +// values populated, for the fields listed in fieldMask. It returns a +// new HostSet containing the updated values, the hosts in the set, +// and a count of the number of records updated. s is not changed. +// +// s must contain a valid PublicId and CatalogId. Name, Description, +// Attributes, and PreferredEndpoints can be updated. Name must be +// unique among all sets associated with a single catalog. +// +// An attribute of s will be set to NULL in the database if the +// attribute in s is the zero value and it is included in fieldMask. +// Note that this does not apply to s.Attributes - a null +// s.Attributes is a no-op for modifications. Rather, if fields need +// to be reset, its field in c.Attributes should individually set to +// null. +// +// Updates are sent to OnUpdateSet with a full copy of both the +// current set, the state of the new set should it be updated, along +// with its parent host catalog and persisted state (can include +// secrets). This is a stateless call and does not affect the final +// record written, but some plugins may perform some actions on this +// call. Update of the record in the database is aborted if this call +// fails. +func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, version uint32, fieldMask []string, opt ...Option) (*HostSet, []*Host, *hostplugin.Plugin, int, error) { + const op = "plugin.(Repository).UpdateSet" + if s == nil { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil HostSet") + } + if s.HostSet == nil { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostSet") + } + if s.PublicId == "" { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + if scopeId == "" { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + if len(fieldMask) == 0 { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "empty field mask") + } + + // Get the old set first. We patch the record first before + // sending it to the DB for updating so that we can run on + // OnUpdateSet. Note that the field masks are still used for + // updating. + sets, plg, err := r.getSets(ctx, s.PublicId, "") + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + if len(sets) == 0 { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("host set id %q not found", s.PublicId)) + } + + if len(sets) != 1 { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.Internal, op, fmt.Sprintf("unexpected amount of sets found, want=1, got=%d", len(sets))) + } + + currentSet := sets[0] + + // Assert the version of the current set to make sure we're + // updating the correct one. + if currentSet.GetVersion() != version { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.VersionMismatch, op, fmt.Sprintf("set version mismatch, want=%d, got=%d", currentSet.GetVersion(), version)) + } + + // Clone the set so that we can set fields. + newSet := currentSet.clone() + var updateAttributes bool + var dbMask, nullFields []string + const ( + endpointOpNoop = "endpointOpNoop" + endpointOpDelete = "endpointOpDelete" + endpointOpUpdate = "endpointOpUpdate" + ) + var endpointOp string = endpointOpNoop + for _, f := range fieldMask { + switch { + case strings.EqualFold("name", f) && s.Name == "": + nullFields = append(nullFields, "name") + newSet.Name = s.Name + case strings.EqualFold("name", f) && s.Name != "": + dbMask = append(dbMask, "name") + newSet.Name = s.Name + case strings.EqualFold("description", f) && s.Description == "": + nullFields = append(nullFields, "description") + newSet.Description = s.Description + case strings.EqualFold("description", f) && s.Description != "": + dbMask = append(dbMask, "description") + newSet.Description = s.Description + case strings.EqualFold("PreferredEndpoints", f) && len(s.PreferredEndpoints) == 0: + endpointOp = endpointOpDelete + newSet.PreferredEndpoints = s.PreferredEndpoints + case strings.EqualFold("PreferredEndpoints", f) && len(s.PreferredEndpoints) != 0: + endpointOp = endpointOpUpdate + newSet.PreferredEndpoints = s.PreferredEndpoints + case strings.EqualFold("attributes", strings.Split(f, ".")[0]): + // Flag attributes for updating. While multiple masks may be + // sent, we only need to do this once. + updateAttributes = true + + default: + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) + } + } + + if updateAttributes { + dbMask = append(dbMask, "attributes") + if s.Attributes != nil { + newSet.Attributes, err = patchstruct.PatchBytes(newSet.Attributes, s.Attributes) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("error in set attribute JSON")) + } + } else { + newSet.Attributes = make([]byte, 0) + } + + // Flag the record as needing a sync since we've updated + // attributes. + dbMask = append(dbMask, "NeedSync") + newSet.NeedSync = true + } + + // Get the host catalog for the set and its persisted data. + catalog, persisted, err := r.getCatalog(ctx, newSet.CatalogId) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("error looking up catalog with id %q", newSet.CatalogId))) + } + + // Assert that the catalog scope ID and supplied scope ID match. + if catalog.ScopeId != scopeId { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("catalog id %q not in scope id %q", newSet.CatalogId, scopeId)) + } + + // Get the plugin client. + plgClient, ok := r.plugins[catalog.GetPluginId()] + if !ok || plgClient == nil { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin %q not available", catalog.GetPluginId())) + } + + // Convert the catalog values to API protobuf values, which is what + // we use for the plugin hook calls. + plgHc, err := toPluginCatalog(ctx, catalog) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + currentPlgSet, err := toPluginSet(ctx, currentSet) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + newPlgSet, err := toPluginSet(ctx, newSet) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + // Get the preferred endpoints to write out. + var preferredEndpoints []interface{} + if endpointOp == endpointOpUpdate { + preferredEndpoints = make([]interface{}, 0, len(newSet.PreferredEndpoints)) + for i, e := range newSet.PreferredEndpoints { + obj, err := host.NewPreferredEndpoint(ctx, newSet.PublicId, uint32(i+1), e) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + preferredEndpoints = append(preferredEndpoints, obj) + } + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + // If the call to the plugin succeeded, we do not want to call it again if + // the transaction failed and is being retried. + var pluginCalledSuccessfully bool + + var setUpdated, preferredEndpointsUpdated bool + var returnedSet *HostSet + var hosts []*Host + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(r db.Reader, w db.Writer) error { + returnedSet = newSet.clone() + msgs := make([]*oplog.Message, 0, len(preferredEndpoints)+2) + ticket, err := w.GetTicket(s) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + if len(dbMask) != 0 || len(nullFields) != 0 { + var hsOplogMsg oplog.Message + numUpdated, err := w.Update( + ctx, + returnedSet, + dbMask, + nullFields, + db.NewOplogMsg(&hsOplogMsg), + db.WithVersion(&version), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + if numUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 set to be updated, got %d", numUpdated)) + } + + setUpdated = true + msgs = append(msgs, &hsOplogMsg) + } + + switch endpointOp { + case endpointOpDelete: + // Delete all old endpoint entries. + var peDeleteOplogMsg oplog.Message + pep := &host.PreferredEndpoint{ + PreferredEndpoint: &store.PreferredEndpoint{ + HostSetId: newSet.PublicId, + Priority: 1, + }, + } + _, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Only append the oplog message if an operation was actually + // performed. + if peDeleteOplogMsg.Message != nil { + preferredEndpointsUpdated = true + msgs = append(msgs, &peDeleteOplogMsg) + } + + case endpointOpUpdate: + // Delete all old endpoint entries. + var peDeleteOplogMsg oplog.Message + pep := &host.PreferredEndpoint{ + PreferredEndpoint: &store.PreferredEndpoint{ + HostSetId: newSet.PublicId, + Priority: 1, + }, + } + _, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Only append the oplog message if an operation was actually + // performed. + if peDeleteOplogMsg.Message != nil { + msgs = append(msgs, &peDeleteOplogMsg) + } + + // Create the new entries. + peCreateOplogMsgs := make([]*oplog.Message, 0, len(preferredEndpoints)) + if err := w.CreateItems(ctx, preferredEndpoints, db.NewOplogMsgs(&peCreateOplogMsgs)); err != nil { + return err + } + + preferredEndpointsUpdated = true + msgs = append(msgs, peCreateOplogMsgs...) + } + + if !setUpdated && preferredEndpointsUpdated { + returnedSet.Version = uint32(version) + 1 + var hsOplogMsg oplog.Message + numUpdated, err := w.Update( + ctx, + returnedSet, + []string{"version"}, + []string{}, + db.NewOplogMsg(&hsOplogMsg), + db.WithVersion(&version), + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + if numUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 set to be updated, got %d", numUpdated)) + } + + msgs = append(msgs, &hsOplogMsg) + } + + if len(msgs) != 0 { + metadata := s.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + } + + if !pluginCalledSuccessfully { + _, err = plgClient.OnUpdateSet(ctx, &plgpb.OnUpdateSetRequest{ + CurrentSet: currentPlgSet, + NewSet: newPlgSet, + Catalog: plgHc, + Persisted: persisted, + }) + if err != nil { + if status.Code(err) != codes.Unimplemented { + return errors.Wrap(ctx, err, op) + } + } + } + + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s: name %s already exists", newSet.PublicId, newSet.Name))) + } + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", newSet.PublicId))) + } + + hosts, err = listHostBySetIds(ctx, r.reader, []string{returnedSet.PublicId}, opt...) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + var numUpdated int + if setUpdated || preferredEndpointsUpdated { + numUpdated = 1 + } + + if updateAttributes { + // Request a host sync since we have updated attributes. + _ = r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0) + } + + return returnedSet, hosts, plg, numUpdated, nil +} + // LookupSet will look up a host set in the repository and return the host set, // as well as host IDs that match. If the host set is not found, it will return // nil, nil, nil. No options are currently supported. @@ -357,8 +698,20 @@ func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { if in == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin") } + + var name, description *wrapperspb.StringValue + if inName := in.GetName(); inName != "" { + name = wrapperspb.String(inName) + } + if inDescription := in.GetDescription(); inDescription != "" { + description = wrapperspb.String(inDescription) + } + hs := &pb.HostSet{ - Id: in.GetPublicId(), + Id: in.GetPublicId(), + Name: name, + Description: description, + PreferredEndpoints: in.GetPreferredEndpoints(), } if in.GetAttributes() != nil { attrs := &structpb.Struct{} diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 017dad03e..c3d71e914 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -11,6 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin/store" @@ -18,6 +19,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" + hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" @@ -25,6 +27,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" ) func TestRepository_CreateSet(t *testing.T) { @@ -352,6 +355,757 @@ func TestRepository_CreateSet(t *testing.T) { }) } +func TestRepository_UpdateSet(t *testing.T) { + ctx := context.Background() + dbConn, _ := db.TestSetup(t, "postgres") + dbRW := db.New(dbConn) + dbWrapper := db.TestWrapper(t) + sched := scheduler.TestScheduler(t, dbConn, dbWrapper) + dbKmsCache := kms.TestKms(t, dbConn, dbWrapper) + _, projectScope := iam.TestScopes(t, iam.TestRepo(t, dbConn, dbWrapper)) + + // Define a plugin "manager", basically just a map with a mock + // plugin in it. This also includes functionality to capture the + // state, and set an error and the returned secrets to nil. Note + // that the way that this is set up means that the tests cannot run + // in parallel, but there could be other factors affecting that as + // well. + var gotOnUpdateCallCount int + var gotOnUpdateSetRequest *plgpb.OnUpdateSetRequest + var pluginError error + testPlugin := hostplg.TestPlugin(t, dbConn, "test") + testPluginMap := map[string]plgpb.HostPluginServiceClient{ + testPlugin.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnUpdateSetFn: func(_ context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) { + gotOnUpdateCallCount++ + gotOnUpdateSetRequest = req + return &plgpb.OnUpdateSetResponse{}, pluginError + }, + }, + }, + } + + // Set up a test catalog and the secrets for it + testCatalog := TestCatalog(t, dbConn, projectScope.PublicId, testPlugin.GetPublicId()) + testCatalogSecret, err := newHostCatalogSecret(ctx, testCatalog.GetPublicId(), mustStruct(map[string]interface{}{ + "one": "two", + })) + require.NoError(t, err) + scopeWrapper, err := dbKmsCache.GetWrapper(ctx, testCatalog.GetScopeId(), kms.KeyPurposeDatabase) + require.NoError(t, err) + require.NoError(t, testCatalogSecret.encrypt(ctx, scopeWrapper)) + testCatalogSecretQ, testCatalogSecretV := testCatalogSecret.upsertQuery() + secretsUpdated, err := dbRW.Exec(ctx, testCatalogSecretQ, testCatalogSecretV) + require.NoError(t, err) + require.Equal(t, 1, secretsUpdated) + + // Create a test duplicate set. We don't use this set, it just + // exists to ensure that we can test for conflicts when setting + // the name. + const testDuplicateSetName = "duplicate-set-name" + testDuplicateSet := TestSet(t, dbConn, dbKmsCache, sched, testCatalog, testPluginMap) + testDuplicateSet.Name = testDuplicateSetName + setsUpdated, err := dbRW.Update(ctx, testDuplicateSet, []string{"name"}, []string{}) + require.NoError(t, err) + require.Equal(t, 1, setsUpdated) + + // Define some helpers here to make the test table more readable. + type changeHostSetFunc func(s *HostSet) *HostSet + + changeSetToNil := func() changeHostSetFunc { + return func(_ *HostSet) *HostSet { + return nil + } + } + + changeEmbeddedSetToNil := func() changeHostSetFunc { + return func(s *HostSet) *HostSet { + s.HostSet = nil + return s + } + } + + changePublicId := func(v string) changeHostSetFunc { + return func(s *HostSet) *HostSet { + s.PublicId = v + return s + } + } + + changeName := func(v string) changeHostSetFunc { + return func(s *HostSet) *HostSet { + s.Name = v + return s + } + } + + changeDescription := func(s string) changeHostSetFunc { + return func(c *HostSet) *HostSet { + c.Description = s + return c + } + } + + changePreferredEndpoints := func(s []string) changeHostSetFunc { + return func(c *HostSet) *HostSet { + c.PreferredEndpoints = s + return c + } + } + + changeAttributes := func(m map[string]interface{}) changeHostSetFunc { + return func(c *HostSet) *HostSet { + c.Attributes = mustMarshal(m) + return c + } + } + + changeAttributesNil := func() changeHostSetFunc { + return func(c *HostSet) *HostSet { + c.Attributes = nil + return c + } + } + + // Define some checks that will be used in the below tests. Some of + // these are re-used, so we define them here. Most of these are + // assertions and no particular one is non-fatal in that they will + // stop execution. Note that these are executed after wantIsErr, so + // if that is set in an individual table test, these will not be + // executed. + // + // Note that we define some state here, similar to how we + // previously defined gotOnUpdateCatalogRequest above next the + // plugin map. + type checkFunc func(t *testing.T, ctx context.Context) + var ( + gotSet *HostSet + gotNumUpdated int + ) + + checkVersion := func(want uint32) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotSet.Version) + } + } + + checkName := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotSet.Name) + } + } + + checkDescription := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotSet.Description) + } + } + + checkPreferredEndpoints := func(want []string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotSet.PreferredEndpoints) + } + } + + checkAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + require := require.New(t) + st := &structpb.Struct{} + require.NoError(proto.Unmarshal(gotSet.Attributes, st)) + assert.Empty(cmp.Diff(mustStruct(want), st, protocmp.Transform())) + } + } + + checkNeedSync := func(want bool) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotSet.NeedSync) + } + } + + checkUpdateSetRequestCurrentNameNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateSetRequest.CurrentSet.Name) + } + } + + checkUpdateSetRequestNewName := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Name) + } + } + + checkUpdateSetRequestNewNameNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateSetRequest.NewSet.Name) + } + } + + checkUpdateSetRequestCurrentDescriptionNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateSetRequest.CurrentSet.Description) + } + } + + checkUpdateSetRequestNewDescription := func(want string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Description) + } + } + + checkUpdateSetRequestNewDescriptionNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateSetRequest.NewSet.Description) + } + } + + checkUpdateSetRequestCurrentPreferredEndpoints := func(want []string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotOnUpdateSetRequest.CurrentSet.PreferredEndpoints) + } + } + + checkUpdateSetRequestNewPreferredEndpoints := func(want []string) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotOnUpdateSetRequest.NewSet.PreferredEndpoints) + } + } + + checkUpdateSetRequestNewPreferredEndpointsNil := func() checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Nil(gotOnUpdateSetRequest.NewSet.PreferredEndpoints) + } + } + + checkUpdateSetRequestCurrentAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.CurrentSet.Attributes, protocmp.Transform())) + } + } + + checkUpdateSetRequestNewAttributes := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.NewSet.Attributes, protocmp.Transform())) + } + } + + checkUpdateSetRequestPersistedSecrets := func(want map[string]interface{}) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.Persisted.Secrets, protocmp.Transform())) + } + } + + checkNumUpdated := func(want int) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.Equal(want, gotNumUpdated) + } + } + + checkVerifySetOplog := func(op oplog.OpType) checkFunc { + return func(t *testing.T, ctx context.Context) { + t.Helper() + assert := assert.New(t) + assert.NoError( + db.TestVerifyOplog( + t, + dbRW, + gotSet.PublicId, + db.WithOperation(op), + db.WithCreateNotBefore(10*time.Second), + ), + ) + } + } + + tests := []struct { + name string + withScopeId *string + withEmptyPluginMap bool + withPluginError error + changeFuncs []changeHostSetFunc + version uint32 + fieldMask []string + wantCheckFuncs []checkFunc + wantIsErr errors.Code + }{ + { + name: "nil set", + changeFuncs: []changeHostSetFunc{changeSetToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil embedded set", + changeFuncs: []changeHostSetFunc{changeEmbeddedSetToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing public id", + changeFuncs: []changeHostSetFunc{changePublicId("")}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing scope id", + withScopeId: func() *string { a := ""; return &a }(), + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty field mask", + fieldMask: nil, // Should be testing on len + wantIsErr: errors.EmptyFieldMask, + }, + { + name: "bad set id", + changeFuncs: []changeHostSetFunc{changePublicId("badid")}, + fieldMask: []string{"name"}, + wantIsErr: errors.RecordNotFound, + }, + { + name: "version mismatch", + changeFuncs: []changeHostSetFunc{changeName("foo")}, + version: 1, + fieldMask: []string{"name"}, + wantIsErr: errors.VersionMismatch, + }, + { + name: "mismatched scope id to catalog scope", + withScopeId: func() *string { a := "badid"; return &a }(), + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "plugin lookup error", + withEmptyPluginMap: true, + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "plugin invocation error", + withPluginError: errors.New(context.Background(), errors.Internal, "TestRepository_UpdateSet/plugin_invocation_error", "test plugin error"), + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "update name (duplicate)", + changeFuncs: []changeHostSetFunc{changeName(testDuplicateSetName)}, + version: 2, + fieldMask: []string{"name"}, + wantIsErr: errors.NotUnique, + }, + { + name: "update name", + changeFuncs: []changeHostSetFunc{changeName("foo")}, + version: 2, + fieldMask: []string{"name"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewName("foo"), + checkName("foo"), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name to same", + changeFuncs: []changeHostSetFunc{changeName("")}, + version: 2, + fieldMask: []string{"name"}, + wantCheckFuncs: []checkFunc{ + checkVersion(2), // Version remains same even though row is updated + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewNameNil(), + checkName(""), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update description", + changeFuncs: []changeHostSetFunc{changeDescription("foo")}, + version: 2, + fieldMask: []string{"description"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentDescriptionNil(), + checkUpdateSetRequestNewDescription("foo"), + checkDescription("foo"), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update description to same", + changeFuncs: []changeHostSetFunc{changeDescription("")}, + version: 2, + fieldMask: []string{"description"}, + wantCheckFuncs: []checkFunc{ + checkVersion(2), // Version remains same even though row is updated + checkUpdateSetRequestCurrentDescriptionNil(), + checkUpdateSetRequestNewDescriptionNil(), + checkDescription(""), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update preferred endpoints", + changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})}, + version: 2, + fieldMask: []string{"PreferredEndpoints"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "clear preferred endpoints", + changeFuncs: []changeHostSetFunc{changePreferredEndpoints(nil)}, + version: 2, + fieldMask: []string{"PreferredEndpoints"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpointsNil(), + checkPreferredEndpoints(nil), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + }, + }, + { + name: "update attributes (add)", + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "baz": "qux", + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(true), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (overwrite)", + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "foo": "baz", + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(true), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (null)", + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "foo": nil, + })}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{}), + checkAttributes(map[string]interface{}{}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(true), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (full null)", + changeFuncs: []changeHostSetFunc{changeAttributesNil()}, + version: 2, + fieldMask: []string{"attributes"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{}), + checkAttributes(map[string]interface{}{}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(true), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (combined)", + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + })}, + version: 2, + fieldMask: []string{"attributes.a", "attributes.foo"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(true), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name and preferred endpoints", + changeFuncs: []changeHostSetFunc{ + changePreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + changeName("foo"), + }, + version: 2, + fieldMask: []string{"name", "PreferredEndpoints"}, + wantCheckFuncs: []checkFunc{ + checkVersion(3), + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewName("foo"), + checkName("foo"), + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + checkNeedSync(false), + checkNumUpdated(1), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + } + + // Create a host that will be verified as belonging to the created set below. + testHostExternIdPrefix := "test-host-external-id" + testHosts := make([]*Host, 3) + for i := 0; i <= 2; i++ { + testHosts[i] = TestHost(t, dbConn, testCatalog.PublicId, fmt.Sprintf("%s-%d", testHostExternIdPrefix, i)) + } + + // Finally define a function for bringing the test subject host + // set. + setupHostSet := func(t *testing.T, ctx context.Context) *HostSet { + t.Helper() + require := require.New(t) + + set := TestSet( + t, + dbConn, + dbKmsCache, + sched, + testCatalog, + testPluginMap, + WithPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + ) + // Set some (default) attributes on our test set + set.Attributes = mustMarshal(map[string]interface{}{ + "foo": "bar", + }) + + // Set some fake sync detail to the set. + set.LastSyncTime = timestamp.New(time.Now()) + set.NeedSync = false + + numSetsUpdated, err := dbRW.Update(ctx, set, []string{"attributes", "LastSyncTime", "NeedSync"}, []string{}) + require.NoError(err) + require.Equal(1, numSetsUpdated) + + // Add some hosts to the host set. + TestSetMembers(t, dbConn, set.PublicId, testHosts) + + t.Cleanup(func() { + t.Helper() + assert := assert.New(t) + n, err := dbRW.Delete(ctx, set) + assert.NoError(err) + assert.Equal(1, n) + }) + + return set + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + origSet := setupHostSet(t, ctx) + + pluginMap := testPluginMap + if tt.withEmptyPluginMap { + pluginMap = make(map[string]plgpb.HostPluginServiceClient) + } + pluginError = tt.withPluginError + t.Cleanup(func() { pluginError = nil }) + repo, err := NewRepository(dbRW, dbRW, dbKmsCache, sched, pluginMap) + require.NoError(err) + require.NotNil(repo) + + workingSet := origSet.clone() + for _, cf := range tt.changeFuncs { + workingSet = cf(workingSet) + } + + scopeId := testCatalog.ScopeId + if tt.withScopeId != nil { + scopeId = *tt.withScopeId + } + + var gotHosts []*Host + var gotPlugin *hostplugin.Plugin + gotSet, gotHosts, gotPlugin, gotNumUpdated, err = repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask) + t.Cleanup(func() { gotOnUpdateCallCount = 0 }) + if tt.wantIsErr != 0 { + require.Equal(db.NoRowsAffected, gotNumUpdated) + require.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + return + } + require.NoError(err) + require.Equal(1, gotOnUpdateCallCount) + t.Cleanup(func() { gotOnUpdateSetRequest = nil }) + + // Quick assertion that the set is not nil and that the plugin + // ID in the catalog referenced by the set matches the plugin + // ID in the returned plugin. + require.NotNil(gotSet) + require.NotNil(gotPlugin) + assert.Equal(testCatalog.PublicId, gotSet.CatalogId) + assert.Equal(testCatalog.PluginId, gotPlugin.PublicId) + + // Also assert that the hosts returned by the request are the ones that belong to the set + wantHostMap := make(map[string]string, len(testHosts)) + for _, h := range testHosts { + wantHostMap[h.PublicId] = h.ExternalId + } + gotHostMap := make(map[string]string, len(gotHosts)) + for _, h := range gotHosts { + gotHostMap[h.PublicId] = h.ExternalId + } + assert.Equal(wantHostMap, gotHostMap) + + // Perform checks + for _, check := range tt.wantCheckFuncs { + check(t, ctx) + } + }) + } +} + func TestRepository_LookupSet(t *testing.T) { ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") From dba7c6b531277b7363c4bd87114f7ff0951b8c28 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Thu, 11 Nov 2021 08:29:13 -0700 Subject: [PATCH 64/71] Fix value of op constant for syncSets --- internal/host/plugin/job_set_sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/host/plugin/job_set_sync.go b/internal/host/plugin/job_set_sync.go index 2bc54ca37..3f7fd6cbf 100644 --- a/internal/host/plugin/job_set_sync.go +++ b/internal/host/plugin/job_set_sync.go @@ -174,7 +174,7 @@ func nextSync(j scheduler.Job) (time.Duration, error) { // syncSets retrieves from their plugins all the host and membership information // for the provided host sets and updates their values in the database. func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { - const op = "plugin.(Repository).Endpoints" + const op = "plugin.(SetSyncJob).syncSets" if len(setIds) == 0 { return errors.New(ctx, errors.InvalidParameter, op, "no set ids") } From 30e9f944a219463cbca2a123ff0294989fcc7ed5 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 11 Nov 2021 11:28:08 -0500 Subject: [PATCH 65/71] Add sync interval to host sets (#1680) This adds a parameter to host sets allowing the sync interval to be user-specified on a per-host-set basis. A value of -1 disables syncing; a value of null uses the current default. --- api/hostsets/host_set.gen.go | 29 +-- api/hostsets/option.gen.go | 12 + globals/fields.go | 1 + internal/cmd/commands/hostsetscmd/funcs.go | 8 + .../cmd/commands/hostsetscmd/plugin_funcs.go | 35 ++- internal/db/read_writer.go | 5 +- .../oss/postgres/20/05_plugin_host.up.sql | 7 +- .../postgres/20/06_preferred_endpoints.up.sql | 1 + internal/gen/controller.swagger.json | 7 +- internal/host/plugin/host_set.go | 49 ++-- internal/host/plugin/host_set_test.go | 6 +- internal/host/plugin/job_set_sync.go | 50 +++-- internal/host/plugin/job_set_sync_test.go | 212 +++++++++++++++++- internal/host/plugin/options.go | 30 ++- internal/host/plugin/options_test.go | 6 + internal/host/plugin/query.go | 22 +- internal/host/plugin/repository_host_set.go | 5 +- .../host/plugin/repository_host_set_test.go | 79 +++++-- internal/host/plugin/store/host.pb.go | 124 +++++----- internal/host/static/store/static.pb.go | 99 ++++++-- internal/libs/patchstruct/patchstruct_test.go | 2 +- .../api/resources/hostsets/v1/host_set.proto | 15 +- .../storage/host/plugin/store/v1/host.proto | 4 + .../storage/host/static/store/v1/static.proto | 7 + .../handlers/host_sets/host_set_service.go | 30 ++- .../host_sets/host_set_service_test.go | 63 +++--- .../api/resources/hostsets/host_set.pb.go | 75 +++++-- 27 files changed, 752 insertions(+), 231 deletions(-) diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index 36a8b6c27..612c9b81b 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -14,20 +14,21 @@ import ( ) type HostSet struct { - Id string `json:"id,omitempty"` - HostCatalogId string `json:"host_catalog_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Plugin *plugins.PluginInfo `json:"plugin,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Type string `json:"type,omitempty"` - HostIds []string `json:"host_ids,omitempty"` - PreferredEndpoints []string `json:"preferred_endpoints,omitempty"` - Attributes map[string]interface{} `json:"attributes,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + HostCatalogId string `json:"host_catalog_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Plugin *plugins.PluginInfo `json:"plugin,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + HostIds []string `json:"host_ids,omitempty"` + PreferredEndpoints []string `json:"preferred_endpoints,omitempty"` + SyncIntervalSeconds int32 `json:"sync_interval_seconds,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } diff --git a/api/hostsets/option.gen.go b/api/hostsets/option.gen.go index eafe5b7fa..a2b25f4dd 100644 --- a/api/hostsets/option.gen.go +++ b/api/hostsets/option.gen.go @@ -121,3 +121,15 @@ func DefaultPreferredEndpoints() Option { o.postMap["preferred_endpoints"] = nil } } + +func WithSyncIntervalSeconds(inSyncIntervalSeconds int32) Option { + return func(o *options) { + o.postMap["sync_interval_seconds"] = inSyncIntervalSeconds + } +} + +func DefaultSyncIntervalSeconds() Option { + return func(o *options) { + o.postMap["sync_interval_seconds"] = nil + } +} diff --git a/globals/fields.go b/globals/fields.go index 1987041b3..684411135 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -59,6 +59,7 @@ const ( ApplicationCredentialSourceIdsField = "application_credential_source_ids" ApplicationCredentialSourcesField = "application_credential_sources" PreferredEndpointsField = "preferred_endpoints" + SyncIntervalSecondsField = "sync_interval_seconds" PluginIdField = "plugin_id" PluginField = "plugin" PluginNameField = "plugin_name" diff --git a/internal/cmd/commands/hostsetscmd/funcs.go b/internal/cmd/commands/hostsetscmd/funcs.go index 532501bf7..c6477b38e 100644 --- a/internal/cmd/commands/hostsetscmd/funcs.go +++ b/internal/cmd/commands/hostsetscmd/funcs.go @@ -215,6 +215,11 @@ func (c *Command) printListTable(items []*hostsets.HostSet) string { fmt.Sprintf(" Description: %s", item.Description), ) } + if item.SyncIntervalSeconds != 0 { + output = append(output, + fmt.Sprintf(" Sync Interval: %d seconds", item.SyncIntervalSeconds), + ) + } if len(item.AuthorizedActions) > 0 { output = append(output, " Authorized Actions:", @@ -256,6 +261,9 @@ func printItemTable(result api.GenericResult) string { if item.PreferredEndpoints != nil { nonAttributeMap["Preferred Endpoints"] = item.PreferredEndpoints } + if item.SyncIntervalSeconds != 0 { + nonAttributeMap["Sync Interval"] = fmt.Sprintf("%d seconds", item.SyncIntervalSeconds) + } maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap) diff --git a/internal/cmd/commands/hostsetscmd/plugin_funcs.go b/internal/cmd/commands/hostsetscmd/plugin_funcs.go index fca90f444..5777357ce 100644 --- a/internal/cmd/commands/hostsetscmd/plugin_funcs.go +++ b/internal/cmd/commands/hostsetscmd/plugin_funcs.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/api/hostsets" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/libs/endpoint" + "github.com/hashicorp/go-secure-stdlib/parseutil" ) func init() { @@ -16,12 +17,13 @@ func init() { type extraPluginCmdVars struct { flagPreferredEndpoints []string + flagSyncInterval string } func extraPluginActionsFlagsMapFuncImpl() map[string][]string { return map[string][]string{ - "create": {"preferred-endpoint"}, - "update": {"preferred-endpoint"}, + "create": {"preferred-endpoint", "sync-interval"}, + "update": {"preferred-endpoint", "sync-interval"}, } } @@ -68,8 +70,19 @@ func extraPluginFlagsFuncImpl(c *PluginCommand, set *base.FlagSets, f *base.Flag `or "dns:", specifying which IP address or DNS name out ` + `of a host's available possibilities should be preferred. May be specified ` + `multiple times, which will build up an in-order set of preferences. ` + - `If no preferences are specified, a value will be chosen _at random_ from ` + - `all available values. May not be valid for all plugin types.`, + `If no preferences are specified, a value will be chosen from among all ` + + `available values using a built-in priority order. May not be valid ` + + `for all plugin types.`, + }) + case "sync-interval": + fs.StringVar(&base.StringVar{ + Name: "sync-interval", + Target: &c.flagSyncInterval, + Usage: `An interger number of seconds, or a string such as "400s", "5m", or "6h", ` + + "indicating the amount of time that should elapse between syncs of the host set. " + + "The interval will be applied to the end of the previous sync operation, not the start. " + + "Setting to any negative value will disable syncing for that host set; setting to null " + + "will cause the set to use Boundary's default. The default may change between releases.", }) } } @@ -93,5 +106,19 @@ func extraPluginFlagsHandlingFuncImpl(c *PluginCommand, _ *base.FlagSets, opts * *opts = append(*opts, hostsets.WithPreferredEndpoints(c.flagPreferredEndpoints)) } + switch c.flagSyncInterval { + case "": + case "null": + *opts = append(*opts, hostsets.DefaultSyncIntervalSeconds()) + + default: + interval, err := parseutil.ParseDurationSecond(c.flagSyncInterval) + if err != nil { + c.UI.Error(fmt.Sprintf("Unable to successfully parse given sync interval: %s", err)) + return false + } + *opts = append(*opts, hostsets.WithSyncIntervalSeconds(int32(interval.Seconds()))) + } + return true } diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 8287b2c5a..26a3ebf43 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -1137,7 +1137,7 @@ func (rw *Db) LookupWhere(ctx context.Context, resource interface{}, where strin // // Supports the WithLimit option. If WithLimit < 0, then unlimited results are returned. // If WithLimit == 0, then default limits are used for results. -// Supports the WithOrder option. +// Supports the WithOrder and WithDebug options. func (rw *Db) SearchWhere(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error { const op = "db.SearchWhere" opts := GetOpts(opt...) @@ -1155,6 +1155,9 @@ func (rw *Db) SearchWhere(ctx context.Context, resources interface{}, where stri if opts.withOrder != "" { db = db.Order(opts.withOrder) } + if opts.withDebug { + db = db.Debug() + } // Perform limiting switch { case opts.WithLimit < 0: // any negative number signals unlimited results diff --git a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql index fefe23f65..f212e1aca 100644 --- a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql @@ -125,6 +125,11 @@ begin; update_time wt_timestamp, last_sync_time wt_timestamp, need_sync bool not null, + sync_interval_seconds int + constraint sync_interval_seconds_not_equal_zero + check(sync_interval_seconds != 0) + constraint sync_interval_seconds_not_less_then_negative_one + check(sync_interval_seconds >= -1), version wt_version, attributes bytea not null, constraint host_plugin_set_catalog_id_name_uq @@ -148,7 +153,7 @@ begin; for each row execute procedure default_create_time(); create trigger immutable_columns before update on host_plugin_set - for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + for each row execute procedure immutable_columns('public_id', 'catalog_id', 'create_time'); create trigger insert_host_set_subtype before insert on host_plugin_set for each row execute procedure insert_host_set_subtype(); diff --git a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql index 94c8a4ad0..2a8ef89cc 100644 --- a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql @@ -60,6 +60,7 @@ create view host_plugin_host_set_with_value_obj as hs.update_time, hs.last_sync_time, hs.need_sync, + hs.sync_interval_seconds, hs.version, hs.attributes, -- the string_agg(..) column will be null if there are no associated value objects diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 441143e83..983b3a9a7 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -4322,7 +4322,12 @@ "items": { "type": "string" }, - "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host." + "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host. Preferences are specified by\n\"cidr:\u003cvalid IPv4/6 CIDR\u003e\" or \"dns:\u003cglobbed name\u003e\", specifying which IP\naddress or DNS name out of a host's available possibilities should be\npreferred. If no preferences are specified, a value will be chosen from\namong all avialable values using a built-in priority order. May not be\nvalid for all plugin types." + }, + "sync_interval_seconds": { + "type": "integer", + "format": "int32", + "description": "An interger number of seconds indicating the amount of time that should\nelapse between syncs of the host set. The interval will be applied to the\nend of the previous sync operation, not the start. Setting to any\nnegative value will disable syncing for that host set; setting to zero\nwill cause the set to use Boundary's default. The default may change\nbetween releases. May not be valid for all plugin types." }, "attributes": { "type": "object", diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index 56efc07b7..193d9b845 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -17,9 +17,10 @@ import ( // A HostSet is a collection of hosts from the set's catalog. type HostSet struct { *store.HostSet - PluginId string `gorm:"-"` - HostIds []string `gorm:"-"` - tableName string `gorm:"-"` + PluginId string `gorm:"-"` + HostIds []string `gorm:"-"` + PreferredEndpoints []string `gorm:"-"` + tableName string `gorm:"-"` } // NewHostSet creates a new in memory HostSet assigned to catalogId. Attributes, @@ -35,12 +36,13 @@ func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, set := &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalogId, - Name: opts.withName, - Description: opts.withDescription, - Attributes: attrs, - PreferredEndpoints: opts.withPreferredEndpoints, + CatalogId: catalogId, + Name: opts.withName, + Description: opts.withDescription, + SyncIntervalSeconds: opts.withSyncIntervalSeconds, + Attributes: attrs, }, + PreferredEndpoints: opts.withPreferredEndpoints, } return set, nil @@ -69,7 +71,8 @@ func allocHostSet() *HostSet { func (s *HostSet) clone() *HostSet { cp := proto.Clone(s.HostSet) hs := &HostSet{ - HostSet: cp.(*store.HostSet), + HostSet: cp.(*store.HostSet), + PreferredEndpoints: s.PreferredEndpoints, } if s.Attributes != nil && len(s.Attributes) == 0 && hs.Attributes == nil { hs.Attributes = []byte{} @@ -92,19 +95,20 @@ func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata { // hostSetAgg is a view that aggregates the host set's value objects in to // string fields delimited with the aggregateDelimiter of "|" type hostSetAgg struct { - PublicId string `gorm:"primary_key"` - CatalogId string - PluginId string - Name string - Description string - CreateTime *timestamp.Timestamp - UpdateTime *timestamp.Timestamp - LastSyncTime *timestamp.Timestamp - NeedSync bool - Version uint32 - Attributes []byte - PreferredEndpoints string - HostIds string + PublicId string `gorm:"primary_key"` + CatalogId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + LastSyncTime *timestamp.Timestamp + NeedSync bool + SyncIntervalSeconds int32 + Version uint32 + Attributes []byte + PreferredEndpoints string + HostIds string } func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { @@ -121,6 +125,7 @@ func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { hs.UpdateTime = agg.UpdateTime hs.LastSyncTime = agg.LastSyncTime hs.NeedSync = agg.NeedSync + hs.SyncIntervalSeconds = agg.SyncIntervalSeconds hs.Version = agg.Version hs.Attributes = agg.Attributes if agg.HostIds != "" { diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index ac9a0d4cb..ae9144cd5 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -150,10 +150,10 @@ func TestHostSet_Create(t *testing.T) { }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: cat.GetPublicId(), - PreferredEndpoints: []string{"cidr:1.2.3.4"}, - Attributes: []byte{}, + CatalogId: cat.GetPublicId(), + Attributes: []byte{}, }, + PreferredEndpoints: []string{"cidr:1.2.3.4"}, }, }, } diff --git a/internal/host/plugin/job_set_sync.go b/internal/host/plugin/job_set_sync.go index 3f7fd6cbf..1b63da46c 100644 --- a/internal/host/plugin/job_set_sync.go +++ b/internal/host/plugin/job_set_sync.go @@ -96,7 +96,7 @@ func (r *SetSyncJob) Run(ctx context.Context) error { // Fetch all sets that will reach their sync point within the syncWindow. // This is done to avoid constantly scheduling the set sync job when there // are multiple sets to sync in sequence. - err := r.reader.SearchWhere(ctx, &setAggs, `need_sync or last_sync_time <= wt_add_seconds_to_now(?)`, []interface{}{-1 * setSyncJobRunInterval.Seconds()}, db.WithLimit(r.limit)) + err := r.reader.SearchWhere(ctx, &setAggs, setSyncJobQuery, []interface{}{-1 * setSyncJobRunInterval.Seconds()}, db.WithLimit(r.limit)) if err != nil { return errors.Wrap(ctx, err, op) } @@ -152,23 +152,35 @@ func nextSync(j scheduler.Job) (time.Duration, error) { } defer rows.Close() - for rows.Next() { - type NextResync struct { - SyncNow bool - ResyncIn time.Duration - } - var n NextResync - err = r.ScanRows(rows, &n) - if err != nil { - return 0, errors.WrapDeprecated(err, op) - } - if n.SyncNow || n.ResyncIn < 0 { - // If we are past the next renewal time, return 0 to schedule immediately - return 0, nil - } - return n.ResyncIn * time.Second, nil + if !rows.Next() { + return setSyncJobRunInterval, nil } - return setSyncJobRunInterval, nil + + type NextResync struct { + SyncNow bool + SyncIntervalSeconds int32 + ResyncIn time.Duration + } + var n NextResync + err = r.ScanRows(rows, &n) + if err != nil { + return 0, errors.WrapDeprecated(err, op) + } + switch { + case n.SyncNow: + // Immediate + return 0, nil + case n.SyncIntervalSeconds < 0: + // In this case automatic syncing is disabled; we still sync if SyncNow + // but otherwise do not. We schedule the job at the default cadence but + // it will do nothing, just calculate a next run time to ensure it + // should stay disabled. + return setSyncJobRunInterval, nil + case n.ResyncIn < 0: + // Immediate + return 0, nil + } + return n.ResyncIn * time.Second, nil } // syncSets retrieves from their plugins all the host and membership information @@ -226,7 +238,7 @@ func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { if !ok { si = &setInfo{} } - si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints()) + si.preferredEndpoint = endpoint.WithPreferenceOrder(s.PreferredEndpoints) si.plgSet, err = toPluginSet(ctx, s) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId()))) @@ -288,7 +300,7 @@ func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { } if _, err := r.upsertHosts(ctx, ci.storeCat, catSetIds, resp.GetHosts()); err != nil { - errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts")) + return errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts")) } updateSyncDataQuery := ` diff --git a/internal/host/plugin/job_set_sync_test.go b/internal/host/plugin/job_set_sync_test.go index 5eb3cb510..66e8705d5 100644 --- a/internal/host/plugin/job_set_sync_test.go +++ b/internal/host/plugin/job_set_sync_test.go @@ -13,8 +13,8 @@ import ( hostplg "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + assertpkg "github.com/stretchr/testify/assert" + requirepkg "github.com/stretchr/testify/require" ) func TestNewSetSyncJob(t *testing.T) { @@ -109,7 +109,7 @@ func TestNewSetSyncJob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) got, err := newSetSyncJob(ctx, tt.args.r, tt.args.w, tt.args.kms, tt.args.plgm, tt.options...) if tt.wantErr { @@ -130,7 +130,7 @@ func TestNewSetSyncJob(t *testing.T) { func TestSetSyncJob_Run(t *testing.T) { t.Parallel() - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) @@ -215,6 +215,136 @@ func TestSetSyncJob_Run(t *testing.T) { require.NoError(rw.LookupByPublicId(ctx, hs)) assert.Greater(hs.GetLastSyncTime().AsTime().UnixNano(), firstSyncTime.AsTime().UnixNano()) assert.False(hs.GetNeedSync()) + + // Now, run a battery of tests with values for SyncIntervalSeconds + type setArgs struct { + syncIntervalSeconds int32 + lastSyncTime *timestamp.Timestamp + needsSync bool + } + tests := []struct { + name string + setArgs setArgs + expectSync bool + }{ + { + name: "never-synced-before-needs-sync-false", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: false, + }, + expectSync: true, + }, + { + name: "never-synced-before-needs-sync-true", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: true, + }, + expectSync: true, + }, + { + name: "never-synced-before-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-just-now", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-just-now-need-sync", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-just-now-need-sync-but-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-30-seconds-ago-default-time", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-30-seconds-ago-custom-time", + setArgs: setArgs{ + syncIntervalSeconds: 5, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: true, + }, + { + name: "synced-30-seconds-ago-custom-larger-time", + setArgs: setArgs{ + syncIntervalSeconds: 90, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-30-seconds-ago-custom-larger-time-need-sync", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: true, + }, + expectSync: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assertpkg.New(t), requirepkg.New(t) + + // Update set + hs.LastSyncTime = tt.setArgs.lastSyncTime + hs.NeedSync = tt.setArgs.needsSync + hs.SyncIntervalSeconds = tt.setArgs.syncIntervalSeconds + fieldMaskPaths := []string{"LastSyncTime", "NeedSync"} + var setToNullPaths []string + if hs.SyncIntervalSeconds == 0 { + setToNullPaths = []string{"SyncIntervalSeconds"} + } else { + fieldMaskPaths = append(fieldMaskPaths, "SyncIntervalSeconds") + } + count, err := rw.Update(ctx, hs, fieldMaskPaths, setToNullPaths) + require.NoError(err) + assert.Equal(1, count) + + // Run job + err = r.Run(context.Background()) + require.NoError(err) + + // Validate results + var expNum int + if tt.expectSync { + expNum = 1 + } + assert.Equal(expNum, r.numSets) + assert.Equal(expNum, r.numProcessed) + }) + } } func TestSetSyncJob_NextRunIn(t *testing.T) { @@ -235,8 +365,9 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { hostSet := TestSet(t, conn, kmsCache, sched, catalog, plgm) type setArgs struct { - lastSyncTime *timestamp.Timestamp - needsSync bool + syncIntervalSeconds int32 + lastSyncTime *timestamp.Timestamp + needsSync bool } tests := []struct { name string @@ -252,6 +383,15 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: 0, }, + { + name: "never-synced-before-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: false, + }, + want: 0, + }, { name: "synced-just-now", setArgs: setArgs{ @@ -260,6 +400,15 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: setSyncJobRunInterval, }, + { + name: "synced-just-now-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 180, + lastSyncTime: timestamp.Now(), + needsSync: false, + }, + want: 3 * time.Minute, + }, { name: "synced-just-now-need-sync", setArgs: setArgs{ @@ -268,24 +417,67 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: 0, }, + { + name: "synced-just-now-need-sync-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + want: 0, + }, + { + name: "synced-a-bit-ago", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: time.Until(time.Now().Add(setSyncJobRunInterval - (4 * time.Minute))), + }, + { + name: "synced-a-bit-ago-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 300, + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: time.Minute, + }, + { + name: "automatic-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: setSyncJobRunInterval, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) r, err := newSetSyncJob(ctx, rw, rw, kmsCache, plgm) assert.NoError(err) require.NotNil(r) hostSet.NeedSync = tt.setArgs.needsSync hostSet.LastSyncTime = tt.setArgs.lastSyncTime - _, err = rw.Update(ctx, hostSet, []string{"LastSyncTime", "NeedSync"}, nil) + hostSet.SyncIntervalSeconds = tt.setArgs.syncIntervalSeconds + fieldMaskPaths := []string{"LastSyncTime", "NeedSync"} + var setToNullPaths []string + if hostSet.SyncIntervalSeconds == 0 { + setToNullPaths = []string{"SyncIntervalSeconds"} + } else { + fieldMaskPaths = append(fieldMaskPaths, "SyncIntervalSeconds") + } + _, err = rw.Update(ctx, hostSet, fieldMaskPaths, setToNullPaths) require.NoError(err) got, err := r.NextRunIn() require.NoError(err) - // Round to time.Minute to account for lost time between updating set and determining next run - assert.Equal(tt.want.Round(time.Minute), got.Round(time.Minute)) + // Round to five seconds to account for lost time between updating set and determining next run + assert.Equal(tt.want.Round(5*time.Second), got.Round(5*time.Second)) }) } } diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index e84d85702..6536cebcf 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -16,17 +16,18 @@ type Option func(*options) // options = how options are represented type options struct { - withPublicId string - withPluginId string - withName string - withDescription string - withAttributes *structpb.Struct - withSecrets *structpb.Struct - withPreferredEndpoints []string - withIpAddresses []string - withDnsNames []string - withLimit int - withSetIds []string + withPublicId string + withPluginId string + withName string + withDescription string + withAttributes *structpb.Struct + withSecrets *structpb.Struct + withPreferredEndpoints []string + withSyncIntervalSeconds int32 + withIpAddresses []string + withDnsNames []string + withLimit int + withSetIds []string } func getDefaultOptions() options { @@ -84,6 +85,13 @@ func WithPreferredEndpoints(with []string) Option { } } +// WithSyncIntervalSeconds provides an optional sync interval, in seconds +func WithSyncIntervalSeconds(with int32) Option { + return func(o *options) { + o.withSyncIntervalSeconds = with + } +} + // withIpAddresses provides an optional list of ip addresses. func withIpAddresses(with []string) Option { return func(o *options) { diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index fce504999..e55321cf1 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -20,6 +20,12 @@ func Test_GetOpts(t *testing.T) { testOpts.withName = "test" assert.Equal(t, opts, testOpts) }) + t.Run("WithSyncIntervalSeconds", func(t *testing.T) { + opts := getOpts(WithSyncIntervalSeconds(5)) + testOpts := getDefaultOptions() + testOpts.withSyncIntervalSeconds = 5 + assert.Equal(t, opts, testOpts) + }) t.Run("WithPluginId", func(t *testing.T) { opts := getOpts(withPluginId("test")) testOpts := getDefaultOptions() diff --git a/internal/host/plugin/query.go b/internal/host/plugin/query.go index 792f4b4ab..700a126cc 100644 --- a/internal/host/plugin/query.go +++ b/internal/host/plugin/query.go @@ -19,10 +19,28 @@ delete from host_plugin_catalog_secret setSyncNextRunInQuery = ` select - need_sync as sync_now, - extract(epoch from (least(now(), last_sync_time) + ?) - now())::int as resync_in + need_sync as sync_now, + sync_interval_seconds, + case + when sync_interval_seconds is null + then + extract(epoch from (least(now(), last_sync_time) + ?) - now())::int + when sync_interval_seconds > 0 + then + extract(epoch from (least(now(), last_sync_time)) - now())::int + sync_interval_seconds + else + 0 + end resync_in from host_plugin_set order by need_sync desc, last_sync_time desc limit 1; +` + + setSyncJobQuery = ` +need_sync + or +sync_interval_seconds is null and last_sync_time <= wt_add_seconds_to_now(?) + or +sync_interval_seconds > 0 and wt_add_seconds(sync_interval_seconds, last_sync_time) <= current_timestamp ` ) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index a4f2bfd94..746115593 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -49,6 +49,9 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, if scopeId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } + if s.SyncIntervalSeconds < -1 { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "invalid sync interval") + } if s.Attributes == nil { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") } @@ -779,7 +782,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En h := ha.toHost() for _, sId := range hostIdToSetIds[h.GetPublicId()] { s := setIdToSet[sId] - pref, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(s.GetPreferredEndpoints())) + pref, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(s.PreferredEndpoints)) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId))) } diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index c3d71e914..514241881 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -46,12 +46,12 @@ func TestRepository_CreateSet(t *testing.T) { attrs := []byte{} tests := []struct { - name string - in *HostSet - opts []Option - want *HostSet + name string + in *HostSet + opts []Option + want *HostSet wantPluginCalled bool - wantIsErr errors.Code + wantIsErr errors.Code }{ { name: "nil-HostSet", @@ -91,6 +91,17 @@ func TestRepository_CreateSet(t *testing.T) { }, wantIsErr: errors.InvalidParameter, }, + { + name: "invalid-sync-interval-too-negative", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -99, + Attributes: attrs, + }, + }, + wantIsErr: errors.InvalidParameter, + }, { name: "valid-no-options", in: &HostSet{ @@ -111,17 +122,17 @@ func TestRepository_CreateSet(t *testing.T) { name: "valid-preferred-endpoints", in: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Attributes: attrs, - PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + CatalogId: catalog.PublicId, + Attributes: attrs, }, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Attributes: attrs, - PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + CatalogId: catalog.PublicId, + Attributes: attrs, }, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, }, wantPluginCalled: true, }, @@ -143,6 +154,42 @@ func TestRepository_CreateSet(t *testing.T) { }, wantPluginCalled: true, }, + { + name: "valid-sync-interval-disabled", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -1, + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -1, + Attributes: attrs, + }, + }, + wantPluginCalled: true, + }, + { + name: "valid-sync-interval-positive", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: 60, + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: 60, + Attributes: attrs, + }, + }, + wantPluginCalled: true, + }, { name: "valid-unimplemented-plugin", in: &HostSet{ @@ -187,7 +234,7 @@ func TestRepository_CreateSet(t *testing.T) { Description: ("test-description-repo"), Attributes: func() []byte { st, err := structpb.NewStruct(map[string]interface{}{ - "k1": "foo", + "k1": "foo", "removed": nil, }) require.NoError(t, err) @@ -1129,7 +1176,7 @@ func TestRepository_LookupSet(t *testing.T) { return &plgpb.ListHostsResponse{}, nil }, }), - }) + }, WithSyncIntervalSeconds(5)) hostSetId, err := newHostSetId(ctx) require.NoError(t, err) @@ -1192,9 +1239,9 @@ func TestRepository_Endpoints(t *testing.T) { } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet10 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) - hostSet192 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) - hostSet100 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) + hostSet10 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet10"), WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) + hostSet192 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet192"), WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) + hostSet100 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet100"), WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) hostlessSet := TestSet(t, conn, kms, sched, hostlessCatalog, plgm) h1 := TestHost(t, conn, catalog.GetPublicId(), "test", withIpAddresses([]string{"10.0.0.5", "192.168.0.5"})) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 8b61078c5..3100beb71 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -195,6 +195,9 @@ type HostSet struct { // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` PreferredEndpoints []string `protobuf:"bytes,11,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` + // Sync interval is a value representing a duration in seconds + // @inject_tag: `gorm:"default:null"` + SyncIntervalSeconds int32 `protobuf:"varint,12,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty" gorm:"default:null"` } func (x *HostSet) Reset() { @@ -306,6 +309,13 @@ func (x *HostSet) GetPreferredEndpoints() []string { return nil } +func (x *HostSet) GetSyncIntervalSeconds() int32 { + if x != nil { + return x.SyncIntervalSeconds + } + return 0 +} + type HostCatalogSecret struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -659,7 +669,7 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xa1, 0x04, 0x0a, 0x07, 0x48, 0x6f, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x87, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -693,61 +703,67 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, - 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x64, 0x0a, + 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, + 0x29, 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, + 0x73, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, + 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, + 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, - 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, - 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, - 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, + 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, + 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/host/static/store/static.pb.go b/internal/host/static/store/static.pb.go index 6388b485b..aad9a2418 100644 --- a/internal/host/static/store/static.pb.go +++ b/internal/host/static/store/static.pb.go @@ -433,6 +433,56 @@ func (x *HostSetMember) GetCatalogId() string { return "" } +// These fields are not implemented yet on the host set. They are captured +// here for the purpose of identifying the mask maps which are on the top level +// api set resource but aren't present in the static host set storage. +type UnimplementedSetFields struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SyncIntervalSeconds int32 `protobuf:"varint,22,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty"` +} + +func (x *UnimplementedSetFields) Reset() { + *x = UnimplementedSetFields{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_static_store_v1_static_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnimplementedSetFields) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnimplementedSetFields) ProtoMessage() {} + +func (x *UnimplementedSetFields) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_static_store_v1_static_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 UnimplementedSetFields.ProtoReflect.Descriptor instead. +func (*UnimplementedSetFields) Descriptor() ([]byte, []int) { + return file_controller_storage_host_static_store_v1_static_proto_rawDescGZIP(), []int{4} +} + +func (x *UnimplementedSetFields) GetSyncIntervalSeconds() int32 { + if x != nil { + return x.SyncIntervalSeconds + } + return 0 +} + var File_controller_storage_host_static_store_v1_static_proto protoreflect.FileDescriptor var file_controller_storage_host_static_store_v1_static_proto_rawDesc = []byte{ @@ -524,7 +574,15 @@ var file_controller_storage_host_static_store_v1_static_proto_rawDesc = []byte{ 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x49, 0x64, 0x22, 0x7e, 0x0a, 0x16, 0x55, 0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x64, 0x0a, 0x15, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, 0x29, + 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, + 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, @@ -543,21 +601,22 @@ func file_controller_storage_host_static_store_v1_static_proto_rawDescGZIP() []b return file_controller_storage_host_static_store_v1_static_proto_rawDescData } -var file_controller_storage_host_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_host_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_controller_storage_host_static_store_v1_static_proto_goTypes = []interface{}{ - (*HostCatalog)(nil), // 0: controller.storage.host.static.store.v1.HostCatalog - (*Host)(nil), // 1: controller.storage.host.static.store.v1.Host - (*HostSet)(nil), // 2: controller.storage.host.static.store.v1.HostSet - (*HostSetMember)(nil), // 3: controller.storage.host.static.store.v1.HostSetMember - (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp + (*HostCatalog)(nil), // 0: controller.storage.host.static.store.v1.HostCatalog + (*Host)(nil), // 1: controller.storage.host.static.store.v1.Host + (*HostSet)(nil), // 2: controller.storage.host.static.store.v1.HostSet + (*HostSetMember)(nil), // 3: controller.storage.host.static.store.v1.HostSetMember + (*UnimplementedSetFields)(nil), // 4: controller.storage.host.static.store.v1.UnimplementedSetFields + (*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_host_static_store_v1_static_proto_depIdxs = []int32{ - 4, // 0: controller.storage.host.static.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 1: controller.storage.host.static.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 2: controller.storage.host.static.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 3: controller.storage.host.static.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 4: controller.storage.host.static.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 5: controller.storage.host.static.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 0: controller.storage.host.static.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 1: controller.storage.host.static.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 2: controller.storage.host.static.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 3: controller.storage.host.static.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 4: controller.storage.host.static.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 5: controller.storage.host.static.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name @@ -619,6 +678,18 @@ func file_controller_storage_host_static_store_v1_static_proto_init() { return nil } } + file_controller_storage_host_static_store_v1_static_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnimplementedSetFields); 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{ @@ -626,7 +697,7 @@ func file_controller_storage_host_static_store_v1_static_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_static_store_v1_static_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/libs/patchstruct/patchstruct_test.go b/internal/libs/patchstruct/patchstruct_test.go index c7a972ba2..efb8f192f 100644 --- a/internal/libs/patchstruct/patchstruct_test.go +++ b/internal/libs/patchstruct/patchstruct_test.go @@ -81,7 +81,7 @@ var testCases = []testCase{ }, { name: "nested with nil", - dst: nil, + dst: nil, src: map[string]interface{}{ "a": "b", "nested": map[string]interface{}{ diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index cb28046e0..126f73392 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -48,9 +48,22 @@ message HostSet { repeated string host_ids = 100 [json_name="host_ids"]; // An ordered list of endpoint preferences used to choose from among - // multiple possible endpoints for a host. + // multiple possible endpoints for a host. Preferences are specified by + // "cidr:" or "dns:", specifying which IP + // address or DNS name out of a host's available possibilities should be + // preferred. If no preferences are specified, a value will be chosen from + // among all avialable values using a built-in priority order. May not be + // valid for all plugin types. repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true]; + // An interger number of seconds indicating the amount of time that should + // elapse between syncs of the host set. The interval will be applied to the + // end of the previous sync operation, not the start. Setting to any + // negative value will disable syncing for that host set; setting to zero + // will cause the set to use Boundary's default. The default may change + // between releases. May not be valid for all plugin types. + google.protobuf.Int32Value sync_interval_seconds = 102 [json_name="sync_interval_seconds", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"sync_interval_seconds" that: "SyncIntervalSeconds"}]; + // The attributes that are applicable for the specific Host Set type. google.protobuf.Struct attributes = 110 [(custom_options.v1.generate_sdk_option) = true]; diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 645cc0c04..5fa3c8337 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -93,6 +93,10 @@ message HostSet { // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` repeated string preferred_endpoints = 11; + + // Sync interval is a value representing a duration in seconds + // @inject_tag: `gorm:"default:null"` + int32 sync_interval_seconds = 12 [(custom_options.v1.mask_mapping) = {this: "SyncIntervalSeconds" that: "sync_interval_seconds"}]; } message HostCatalogSecret { diff --git a/internal/proto/local/controller/storage/host/static/store/v1/static.proto b/internal/proto/local/controller/storage/host/static/store/v1/static.proto index 13895f7b5..daf3849da 100644 --- a/internal/proto/local/controller/storage/host/static/store/v1/static.proto +++ b/internal/proto/local/controller/storage/host/static/store/v1/static.proto @@ -117,3 +117,10 @@ message HostSetMember { // @inject_tag: `gorm:"default:null"` string catalog_id = 3; } + +// These fields are not implemented yet on the host set. They are captured +// here for the purpose of identifying the mask maps which are on the top level +// api set resource but aren't present in the static host set storage. +message UnimplementedSetFields { + int32 sync_interval_seconds = 22 [(custom_options.v1.mask_mapping) = {this: "SyncIntervalSeconds" that: "sync_interval_seconds"}]; +} \ No newline at end of file diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 1bd93bf6e..36acd7c73 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/host/plugin" plugstore "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/host/static" - "github.com/hashicorp/boundary/internal/host/static/store" + staticstore "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/perms" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" @@ -32,7 +32,7 @@ import ( ) var ( - maskManager handlers.MaskManager + maskManager = map[subtypes.Subtype]handlers.MaskManager{} // IdActions contains the set of actions that can be performed on // individual resources @@ -64,7 +64,10 @@ var ( func init() { var err error - if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.HostSet{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { + if maskManager[static.Subtype], err = handlers.NewMaskManager(handlers.MaskDestination{&staticstore.HostSet{}, &staticstore.UnimplementedSetFields{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { + panic(err) + } + if maskManager[plugin.Subtype], err = handlers.NewMaskManager(handlers.MaskDestination{&plugstore.HostSet{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { panic(err) } } @@ -517,7 +520,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, re return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update")) } h.PublicId = req.GetId() - dbMask := maskManager.Translate(req.GetUpdateMask().GetPaths()) + dbMask := maskManager[static.Subtype].Translate(req.GetUpdateMask().GetPaths()) if len(dbMask) == 0 { return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) } @@ -817,7 +820,10 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler switch h := in.(type) { case *plugin.HostSet: if outputFields.Has(globals.PreferredEndpointsField) { - out.PreferredEndpoints = h.GetPreferredEndpoints() + out.PreferredEndpoints = h.PreferredEndpoints + } + if outputFields.Has(globals.SyncIntervalSecondsField) && h.GetSyncIntervalSeconds() != 0 { + out.SyncIntervalSeconds = &wrapperspb.Int32Value{Value: h.GetSyncIntervalSeconds()} } if outputFields.Has(globals.AttributesField) { attrs := &structpb.Struct{} @@ -865,6 +871,9 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) if item.GetPreferredEndpoints() != nil { opts = append(opts, plugin.WithPreferredEndpoints(item.GetPreferredEndpoints())) } + if item.GetSyncIntervalSeconds() != nil { + opts = append(opts, plugin.WithSyncIntervalSeconds(item.GetSyncIntervalSeconds().GetValue())) + } hs, err := plugin.NewHostSet(ctx, catalogId, opts...) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) @@ -904,6 +913,11 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) e if req.GetItem().GetType() != "" && req.GetItem().GetType() != plugin.Subtype.String() { badFields[globals.TypeField] = "Doesn't match the parent resource's type." } + if val := req.GetItem().GetSyncIntervalSeconds(); val != nil { + if val.GetValue() == 0 || val.GetValue() < -1 { + badFields[globals.SyncIntervalSecondsField] = "Must be -1 or a positive integer." + } + } } return badFields }) @@ -923,6 +937,12 @@ func validateUpdateRequest(ctx context.Context, req *pbs.UpdateHostSetRequest) e if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { badFields[globals.TypeField] = "Cannot modify the resource type." } + case plugin.Subtype: + if val := req.GetItem().GetSyncIntervalSeconds(); val != nil { + if val.GetValue() == 0 || val.GetValue() < -1 { + badFields[globals.SyncIntervalSecondsField] = "Must be -1 or a positive integer." + } + } } return badFields }, static.HostSetPrefix, plugin.HostSetPrefix) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 1122ee407..5d0c7ae4d 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -37,6 +37,7 @@ import ( "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" ) var testAuthorizedActions = map[subtypes.Subtype][]string{ @@ -168,7 +169,7 @@ func TestGet_Plugin(t *testing.T) { } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints)) + hs := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints), plugin.WithSyncIntervalSeconds(-1)) toMerge := &pbs.GetHostSetRequest{} @@ -184,8 +185,9 @@ func TestGet_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - PreferredEndpoints: prefEndpoints, - AuthorizedActions: testAuthorizedActions[plugin.Subtype], + PreferredEndpoints: prefEndpoints, + SyncIntervalSeconds: &wrappers.Int32Value{Value: -1}, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], } cases := []struct { @@ -373,7 +375,7 @@ func TestList_Plugin(t *testing.T) { var wantHs []*pb.HostSet for i := 0; i < 10; i++ { - h := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) + h := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints), plugin.WithSyncIntervalSeconds(5)) wantHs = append(wantHs, &pb.HostSet{ Id: h.GetPublicId(), HostCatalogId: h.GetCatalogId(), @@ -383,12 +385,13 @@ func TestList_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - CreatedTime: h.GetCreateTime().GetTimestamp(), - UpdatedTime: h.GetUpdateTime().GetTimestamp(), - Version: h.GetVersion(), - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], - PreferredEndpoints: preferredEndpoints, + CreatedTime: h.GetCreateTime().GetTimestamp(), + UpdatedTime: h.GetUpdateTime().GetTimestamp(), + Version: h.GetVersion(), + Type: plugin.Subtype.String(), + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + PreferredEndpoints: preferredEndpoints, + SyncIntervalSeconds: &wrappers.Int32Value{Value: 5}, }) } @@ -856,10 +859,11 @@ func TestCreate_Plugin(t *testing.T) { { name: "No Attributes", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Name: &wrappers.StringValue{Value: "No Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: -1}, }}, res: &pbs.CreateHostSetResponse{ Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), @@ -871,21 +875,23 @@ func TestCreate_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - Name: &wrappers.StringValue{Value: "No Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: -1}, }, }, }, { name: "With Attributes", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Name: &wrappers.StringValue{Value: "With Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - Attributes: testInputAttrs, + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: 90}, + Attributes: testInputAttrs, }}, res: &pbs.CreateHostSetResponse{ Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), @@ -897,11 +903,12 @@ func TestCreate_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - Name: &wrappers.StringValue{Value: "With Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], - Attributes: testOutputAttrs, + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: 90}, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + Attributes: testOutputAttrs, }, }, }, diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index ff4bcb274..6c883dede 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -56,8 +56,20 @@ type HostSet struct { // Output only. A list of Hosts in this Host Set. HostIds []string `protobuf:"bytes,100,rep,name=host_ids,proto3" json:"host_ids,omitempty"` // An ordered list of endpoint preferences used to choose from among - // multiple possible endpoints for a host. + // multiple possible endpoints for a host. Preferences are specified by + // "cidr:" or "dns:", specifying which IP + // address or DNS name out of a host's available possibilities should be + // preferred. If no preferences are specified, a value will be chosen from + // among all avialable values using a built-in priority order. May not be + // valid for all plugin types. PreferredEndpoints []string `protobuf:"bytes,101,rep,name=preferred_endpoints,proto3" json:"preferred_endpoints,omitempty"` + // An interger number of seconds indicating the amount of time that should + // elapse between syncs of the host set. The interval will be applied to the + // end of the previous sync operation, not the start. Setting to any + // negative value will disable syncing for that host set; setting to zero + // will cause the set to use Boundary's default. The default may change + // between releases. May not be valid for all plugin types. + SyncIntervalSeconds *wrapperspb.Int32Value `protobuf:"bytes,102,opt,name=sync_interval_seconds,proto3" json:"sync_interval_seconds,omitempty"` // The attributes that are applicable for the specific Host Set type. Attributes *structpb.Struct `protobuf:"bytes,110,opt,name=attributes,proto3" json:"attributes,omitempty"` // Output only. The available actions on this resource for this user. @@ -180,6 +192,13 @@ func (x *HostSet) GetPreferredEndpoints() []string { return nil } +func (x *HostSet) GetSyncIntervalSeconds() *wrapperspb.Int32Value { + if x != nil { + return x.SyncIntervalSeconds + } + return nil +} + func (x *HostSet) GetAttributes() *structpb.Struct { if x != nil { return x.Attributes @@ -217,7 +236,7 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x06, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, @@ -257,20 +276,28 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, - 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, - 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x34, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x2c, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x15, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, + 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -292,7 +319,8 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_goTypes = []interfa (*plugins.PluginInfo)(nil), // 2: controller.api.resources.plugins.v1.PluginInfo (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 5: google.protobuf.Struct + (*wrapperspb.Int32Value)(nil), // 5: google.protobuf.Int32Value + (*structpb.Struct)(nil), // 6: google.protobuf.Struct } var file_controller_api_resources_hostsets_v1_host_set_proto_depIdxs = []int32{ 1, // 0: controller.api.resources.hostsets.v1.HostSet.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo @@ -301,12 +329,13 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_depIdxs = []int32{ 3, // 3: controller.api.resources.hostsets.v1.HostSet.description:type_name -> google.protobuf.StringValue 4, // 4: controller.api.resources.hostsets.v1.HostSet.created_time:type_name -> google.protobuf.Timestamp 4, // 5: controller.api.resources.hostsets.v1.HostSet.updated_time:type_name -> google.protobuf.Timestamp - 5, // 6: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 5, // 6: controller.api.resources.hostsets.v1.HostSet.sync_interval_seconds:type_name -> google.protobuf.Int32Value + 6, // 7: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_controller_api_resources_hostsets_v1_host_set_proto_init() } From 29b4537d3b112a38e8baa0d657b78b95b0cae4ec Mon Sep 17 00:00:00 2001 From: Todd Date: Thu, 11 Nov 2021 13:51:52 -0700 Subject: [PATCH 66/71] Reference PreferredEndpoints by top level struct name (#1688) * Reference PreferredEndpoints by top level struct name instead of embedded proto GetPreferredEndpoint method. Reorganize the sql files a little. * Make PreferredEndpoints replayable. Test refactoring to remove variables that persist across test calls. DeleteItems instead of trying to use WithWhere. --- .../oss/postgres/20/05_plugin_host.up.sql | 98 ++-- .../postgres/20/06_plugin_host_views.up.sql | 85 ++++ .../postgres/20/06_preferred_endpoints.up.sql | 78 --- internal/host/plugin/repository_host_set.go | 52 +- .../host/plugin/repository_host_set_test.go | 446 +++++++++--------- internal/host/preferred_endpoint.go | 4 +- internal/host/preferred_endpoint_test.go | 8 +- 7 files changed, 382 insertions(+), 389 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/20/06_plugin_host_views.up.sql delete mode 100644 internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql index f212e1aca..a1f655389 100644 --- a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql @@ -335,63 +335,55 @@ begin; create trigger insert_host_plugin_set_member before insert on host_plugin_set_member for each row execute procedure insert_host_plugin_set_member(); + create table host_set_preferred_endpoint ( + create_time wt_timestamp, + host_set_id wt_public_id not null + constraint host_set_fkey + references host_set(public_id) + on delete cascade + on update cascade, + priority wt_priority, + condition text not null + constraint condition_must_not_be_too_short + check(length(trim(condition)) > 4) -- minimum is 'dns:*' + constraint condition_must_not_be_too_long + check(length(trim(condition)) < 255) + constraint condition_has_valid_prefix + check( + left(trim(condition), 4) = 'dns:' + or + left(trim(condition), 5) = 'cidr:' + ) + constraint condition_does_not_contain_invalid_chars + check( + position('|' in trim(condition)) = 0 + and + position('=' in trim(condition)) = 0 + ), + primary key(host_set_id, priority), + constraint host_set_preferred_endpoint_host_set_id_condition_uq + unique(host_set_id, condition) + ); + + -- host_set_immutable_preferred_endpoint() ensures that endpoint conditions + -- assigned to host sets are immutable. + create function + host_set_immutable_preferred_endpoint() + returns trigger + as $$ + begin + raise exception 'preferred endpoints are immutable'; + end; + $$ language plpgsql; + + create trigger immutable_preferred_endpoint + before update on host_set_preferred_endpoint + for each row execute procedure host_set_immutable_preferred_endpoint(); + insert into oplog_ticket (name, version) values ('host_plugin_catalog', 1), - ('host_plugin_catalog_secret', 1), ('host_plugin_set', 1), ('host_plugin_host', 1); - -- host_plugin_catalog_with_secret is useful for reading a plugin catalog with - -- its associated persisted data. - create view host_plugin_catalog_with_secret as - select - hc.public_id, - hc.scope_id, - hc.plugin_id, - hc.name, - hc.description, - hc.create_time, - hc.update_time, - hc.version, - hc.attributes, - hcs.secret, - hcs.key_id, - hcs.create_time as persisted_create_time, - hcs.update_time as persisted_update_time - from - host_plugin_catalog hc - left outer join host_plugin_catalog_secret hcs on hc.public_id = hcs.catalog_id; - comment on view host_plugin_catalog_with_secret is - 'host plugin catalog with its associated persisted data'; - - -- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a - -- plugin host with its associated value objects (ip addresses, dns names) and - -- set membership as columns with delimited values. The delimiter depends on - -- the value objects (e.g. if they need ordering). - create view host_plugin_host_with_value_obj_and_set_memberships as - select - h.public_id, - h.catalog_id, - h.external_id, - hc.scope_id, - hc.plugin_id, - h.name, - h.description, - h.create_time, - h.update_time, - -- the string_agg(..) column will be null if there are no associated value objects - string_agg(distinct host(hip.address), '|') as ip_addresses, - string_agg(distinct hdns.name, '|') as dns_names, - string_agg(distinct hpsm.set_id, '|') as set_ids - from - host_plugin_host h - join host_plugin_catalog hc on h.catalog_id = hc.public_id - left outer join host_ip_address hip on h.public_id = hip.host_id - left outer join host_dns_name hdns on h.public_id = hdns.host_id - left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id - group by h.public_id, hc.plugin_id, hc.scope_id; - comment on view host_plugin_host_with_value_obj_and_set_memberships is - 'host plugin host with its associated value objects'; - commit; diff --git a/internal/db/schema/migrations/oss/postgres/20/06_plugin_host_views.up.sql b/internal/db/schema/migrations/oss/postgres/20/06_plugin_host_views.up.sql new file mode 100644 index 000000000..f3e476457 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/20/06_plugin_host_views.up.sql @@ -0,0 +1,85 @@ +begin; + +-- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its +-- associated value objects (preferred endpoints) as columns with delimited +-- values. The delimiter depends on the value objects (e.g. if they need +-- ordering). +create view host_plugin_host_set_with_value_obj as + select + hs.public_id, + hs.catalog_id, + hc.plugin_id, + hs.name, + hs.description, + hs.create_time, + hs.update_time, + hs.last_sync_time, + hs.need_sync, + hs.sync_interval_seconds, + hs.version, + hs.attributes, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints, + string_agg(distinct hpsm.host_id, '|') as host_ids + from + host_plugin_set hs + join host_plugin_catalog hc on hs.catalog_id = hc.public_id + left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id + left outer join host_plugin_set_member hpsm on hs.public_id = hpsm.set_id + group by hs.public_id, hc.plugin_id; +comment on view host_plugin_host_set_with_value_obj is +'host plugin host set with its associated value objects'; + +-- host_plugin_catalog_with_secret is useful for reading a plugin catalog with +-- its associated persisted data. +create view host_plugin_catalog_with_secret as +select + hc.public_id, + hc.scope_id, + hc.plugin_id, + hc.name, + hc.description, + hc.create_time, + hc.update_time, + hc.version, + hc.attributes, + hcs.secret, + hcs.key_id, + hcs.create_time as persisted_create_time, + hcs.update_time as persisted_update_time +from + host_plugin_catalog hc + left outer join host_plugin_catalog_secret hcs on hc.public_id = hcs.catalog_id; +comment on view host_plugin_catalog_with_secret is + 'host plugin catalog with its associated persisted data'; + +-- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a +-- plugin host with its associated value objects (ip addresses, dns names) and +-- set membership as columns with delimited values. The delimiter depends on +-- the value objects (e.g. if they need ordering). +create view host_plugin_host_with_value_obj_and_set_memberships as +select + h.public_id, + h.catalog_id, + h.external_id, + hc.scope_id, + hc.plugin_id, + h.name, + h.description, + h.create_time, + h.update_time, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct host(hip.address), '|') as ip_addresses, + string_agg(distinct hdns.name, '|') as dns_names, + string_agg(distinct hpsm.set_id, '|') as set_ids +from + host_plugin_host h + join host_plugin_catalog hc on h.catalog_id = hc.public_id + left outer join host_ip_address hip on h.public_id = hip.host_id + left outer join host_dns_name hdns on h.public_id = hdns.host_id + left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id +group by h.public_id, hc.plugin_id, hc.scope_id; +comment on view host_plugin_host_with_value_obj_and_set_memberships is + 'host plugin host with its associated value objects'; + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql deleted file mode 100644 index 2a8ef89cc..000000000 --- a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql +++ /dev/null @@ -1,78 +0,0 @@ -begin; - -create table host_set_preferred_endpoint ( - create_time wt_timestamp, - host_set_id wt_public_id not null - constraint host_set_fkey - references host_set(public_id) - on delete cascade - on update cascade, - priority wt_priority, - condition text not null - constraint condition_must_not_be_too_short - check(length(trim(condition)) > 4) -- minimum is 'dns:*' - constraint condition_must_not_be_too_long - check(length(trim(condition)) < 255) - constraint condition_has_valid_prefix - check( - left(trim(condition), 4) = 'dns:' - or - left(trim(condition), 5) = 'cidr:' - ) - constraint condition_does_not_contain_invalid_chars - check( - position('|' in trim(condition)) = 0 - and - position('=' in trim(condition)) = 0 - ), - primary key(host_set_id, priority), - constraint host_set_preferred_endpoint_host_set_id_condition_uq - unique(host_set_id, condition) -); - --- host_set_immutable_preferred_endpoint() ensures that endpoint conditions --- assigned to host sets are immutable. -create function - host_set_immutable_preferred_endpoint() - returns trigger -as $$ -begin - raise exception 'preferred endpoints are immutable'; -end; -$$ language plpgsql; - -create trigger immutable_preferred_endpoint - before update on host_set_preferred_endpoint - for each row execute procedure host_set_immutable_preferred_endpoint(); - --- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its --- associated value objects (preferred endpoints) as columns with delimited --- values. The delimiter depends on the value objects (e.g. if they need --- ordering). -create view host_plugin_host_set_with_value_obj as - select - hs.public_id, - hs.catalog_id, - hc.plugin_id, - hs.name, - hs.description, - hs.create_time, - hs.update_time, - hs.last_sync_time, - hs.need_sync, - hs.sync_interval_seconds, - hs.version, - hs.attributes, - -- the string_agg(..) column will be null if there are no associated value objects - string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints, - string_agg(distinct hpsm.host_id, '|') as host_ids - from - host_plugin_set hs - join host_plugin_catalog hc on hs.catalog_id = hc.public_id - left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id - left outer join host_plugin_set_member hpsm on hs.public_id = hpsm.set_id - group by hs.public_id, hc.plugin_id; -comment on view host_plugin_host_set_with_value_obj is -'host plugin host set with its associated value objects'; - -commit; \ No newline at end of file diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 746115593..40988766e 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/host" - "github.com/hashicorp/boundary/internal/host/store" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/libs/patchstruct" @@ -358,9 +357,9 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, ctx, db.StdRetryCnt, db.ExpBackoff{}, - func(r db.Reader, w db.Writer) error { + func(reader db.Reader, w db.Writer) error { returnedSet = newSet.clone() - msgs := make([]*oplog.Message, 0, len(preferredEndpoints)+2) + msgs := make([]*oplog.Message, 0, len(preferredEndpoints) + len(currentSet.PreferredEndpoints) + 2) ticket, err := w.GetTicket(s) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) @@ -391,43 +390,43 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, switch endpointOp { case endpointOpDelete: // Delete all old endpoint entries. - var peDeleteOplogMsg oplog.Message - pep := &host.PreferredEndpoint{ - PreferredEndpoint: &store.PreferredEndpoint{ - HostSetId: newSet.PublicId, - Priority: 1, - }, + var peps []interface{} + for i := 1; i <= len(currentSet.PreferredEndpoints); i++ { + p := host.AllocPreferredEndpoint() + p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i) + peps = append(peps, p) } - _, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg)) + deleteOplogMsgs := make([]*oplog.Message, 0, len(peps)) + _, err := w.DeleteItems(ctx, peps, db.NewOplogMsgs(&deleteOplogMsgs)) if err != nil { return errors.Wrap(ctx, err, op) } // Only append the oplog message if an operation was actually // performed. - if peDeleteOplogMsg.Message != nil { + if len(deleteOplogMsgs) > 0 { preferredEndpointsUpdated = true - msgs = append(msgs, &peDeleteOplogMsg) + msgs = append(msgs, deleteOplogMsgs...) } case endpointOpUpdate: // Delete all old endpoint entries. - var peDeleteOplogMsg oplog.Message - pep := &host.PreferredEndpoint{ - PreferredEndpoint: &store.PreferredEndpoint{ - HostSetId: newSet.PublicId, - Priority: 1, - }, + var peps []interface{} + for i := 1; i <= len(currentSet.PreferredEndpoints); i++ { + p := host.AllocPreferredEndpoint() + p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i) + peps = append(peps, p) } - _, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg)) + deleteOplogMsgs := make([]*oplog.Message, 0, len(peps)) + _, err := w.DeleteItems(ctx, peps, db.WithDebug(true), db.NewOplogMsgs(&deleteOplogMsgs)) if err != nil { return errors.Wrap(ctx, err, op) } // Only append the oplog message if an operation was actually // performed. - if peDeleteOplogMsg.Message != nil { - msgs = append(msgs, &peDeleteOplogMsg) + if len(deleteOplogMsgs) > 0 { + msgs = append(msgs, deleteOplogMsgs...) } // Create the new entries. @@ -469,6 +468,15 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, } } + hsAgg := &hostSetAgg{PublicId: currentSet.GetPublicId()} + if err := reader.LookupByPublicId(ctx, hsAgg); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("looking up host after update")) + } + returnedSet, err = hsAgg.toHostSet(ctx) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("converting from host aggregate to host after update")) + } + if !pluginCalledSuccessfully { _, err = plgClient.OnUpdateSet(ctx, &plgpb.OnUpdateSetRequest{ CurrentSet: currentPlgSet, @@ -714,7 +722,7 @@ func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) { Id: in.GetPublicId(), Name: name, Description: description, - PreferredEndpoints: in.GetPreferredEndpoints(), + PreferredEndpoints: in.PreferredEndpoints, } if in.GetAttributes() != nil { attrs := &structpb.Struct{} diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 514241881..6c1ce6302 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -19,7 +19,6 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" hostplg "github.com/hashicorp/boundary/internal/plugin/host" - hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/stretchr/testify/assert" @@ -411,26 +410,9 @@ func TestRepository_UpdateSet(t *testing.T) { dbKmsCache := kms.TestKms(t, dbConn, dbWrapper) _, projectScope := iam.TestScopes(t, iam.TestRepo(t, dbConn, dbWrapper)) - // Define a plugin "manager", basically just a map with a mock - // plugin in it. This also includes functionality to capture the - // state, and set an error and the returned secrets to nil. Note - // that the way that this is set up means that the tests cannot run - // in parallel, but there could be other factors affecting that as - // well. - var gotOnUpdateCallCount int - var gotOnUpdateSetRequest *plgpb.OnUpdateSetRequest - var pluginError error testPlugin := hostplg.TestPlugin(t, dbConn, "test") - testPluginMap := map[string]plgpb.HostPluginServiceClient{ - testPlugin.GetPublicId(): &WrappingPluginClient{ - Server: &TestPluginServer{ - OnUpdateSetFn: func(_ context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) { - gotOnUpdateCallCount++ - gotOnUpdateSetRequest = req - return &plgpb.OnUpdateSetResponse{}, pluginError - }, - }, - }, + dummyPluginMap := map[string]plgpb.HostPluginServiceClient{ + testPlugin.GetPublicId(): &WrappingPluginClient{Server: &plgpb.UnimplementedHostPluginServiceServer{}}, } // Set up a test catalog and the secrets for it @@ -451,7 +433,7 @@ func TestRepository_UpdateSet(t *testing.T) { // exists to ensure that we can test for conflicts when setting // the name. const testDuplicateSetName = "duplicate-set-name" - testDuplicateSet := TestSet(t, dbConn, dbKmsCache, sched, testCatalog, testPluginMap) + testDuplicateSet := TestSet(t, dbConn, dbKmsCache, sched, testCatalog, dummyPluginMap) testDuplicateSet.Name = testDuplicateSetName setsUpdated, err := dbRW.Update(ctx, testDuplicateSet, []string{"name"}, []string{}) require.NoError(t, err) @@ -525,176 +507,60 @@ func TestRepository_UpdateSet(t *testing.T) { // Note that we define some state here, similar to how we // previously defined gotOnUpdateCatalogRequest above next the // plugin map. - type checkFunc func(t *testing.T, ctx context.Context) - var ( - gotSet *HostSet - gotNumUpdated int - ) + type checkHostSetFunc func(t *testing.T, got *HostSet) - checkVersion := func(want uint32) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkVersion := func(want uint32) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Equal(want, gotSet.Version) + assert.Equal(t, want, got.Version, "checkVersion") } } - checkName := func(want string) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkName := func(want string) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Equal(want, gotSet.Name) + assert.Equal(t, want, got.Name, "checkName") } } - checkDescription := func(want string) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkDescription := func(want string) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Equal(want, gotSet.Description) + assert.Equal(t, want, got.Description, "checkDescription") } } - checkPreferredEndpoints := func(want []string) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkPreferredEndpoints := func(want []string) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Equal(want, gotSet.PreferredEndpoints) + assert.Equal(t, want, got.PreferredEndpoints, "checkPreferredEndpoints") } } - checkAttributes := func(want map[string]interface{}) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkAttributes := func(want map[string]interface{}) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - require := require.New(t) st := &structpb.Struct{} - require.NoError(proto.Unmarshal(gotSet.Attributes, st)) - assert.Empty(cmp.Diff(mustStruct(want), st, protocmp.Transform())) + require.NoError(t, proto.Unmarshal(got.Attributes, st)) + assert.Empty(t, cmp.Diff(mustStruct(want), st, protocmp.Transform())) } } - checkNeedSync := func(want bool) checkFunc { - return func(t *testing.T, ctx context.Context) { + checkNeedSync := func(want bool) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Equal(want, gotSet.NeedSync) + assert.Equal(t, want, got.NeedSync, "checkNeedSync") } } - checkUpdateSetRequestCurrentNameNil := func() checkFunc { - return func(t *testing.T, ctx context.Context) { + checkVerifySetOplog := func(op oplog.OpType) checkHostSetFunc { + return func(t *testing.T, got *HostSet) { t.Helper() - assert := assert.New(t) - assert.Nil(gotOnUpdateSetRequest.CurrentSet.Name) - } - } - - checkUpdateSetRequestNewName := func(want string) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Name) - } - } - - checkUpdateSetRequestNewNameNil := func() checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Nil(gotOnUpdateSetRequest.NewSet.Name) - } - } - - checkUpdateSetRequestCurrentDescriptionNil := func() checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Nil(gotOnUpdateSetRequest.CurrentSet.Description) - } - } - - checkUpdateSetRequestNewDescription := func(want string) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Description) - } - } - - checkUpdateSetRequestNewDescriptionNil := func() checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Nil(gotOnUpdateSetRequest.NewSet.Description) - } - } - - checkUpdateSetRequestCurrentPreferredEndpoints := func(want []string) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Equal(want, gotOnUpdateSetRequest.CurrentSet.PreferredEndpoints) - } - } - - checkUpdateSetRequestNewPreferredEndpoints := func(want []string) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Equal(want, gotOnUpdateSetRequest.NewSet.PreferredEndpoints) - } - } - - checkUpdateSetRequestNewPreferredEndpointsNil := func() checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Nil(gotOnUpdateSetRequest.NewSet.PreferredEndpoints) - } - } - - checkUpdateSetRequestCurrentAttributes := func(want map[string]interface{}) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.CurrentSet.Attributes, protocmp.Transform())) - } - } - - checkUpdateSetRequestNewAttributes := func(want map[string]interface{}) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.NewSet.Attributes, protocmp.Transform())) - } - } - - checkUpdateSetRequestPersistedSecrets := func(want map[string]interface{}) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.Persisted.Secrets, protocmp.Transform())) - } - } - - checkNumUpdated := func(want int) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.Equal(want, gotNumUpdated) - } - } - - checkVerifySetOplog := func(op oplog.OpType) checkFunc { - return func(t *testing.T, ctx context.Context) { - t.Helper() - assert := assert.New(t) - assert.NoError( + assert.NoError(t, db.TestVerifyOplog( t, dbRW, - gotSet.PublicId, + got.PublicId, db.WithOperation(op), db.WithCreateNotBefore(10*time.Second), ), @@ -702,6 +568,92 @@ func TestRepository_UpdateSet(t *testing.T) { } } + type checkPluginReqFunc func(t *testing.T, got *plgpb.OnUpdateSetRequest) + + checkUpdateSetRequestCurrentNameNil := func() checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Nil(t, got.CurrentSet.Name, "checkUpdateSetRequestCurrentNameNil") + } + } + + checkUpdateSetRequestNewName := func(want string) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Equal(t, wrapperspb.String(want), got.NewSet.Name, "checkUpdateSetRequestNewName") + } + } + + checkUpdateSetRequestNewNameNil := func() checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Nil(t, got.NewSet.Name, "checkUpdateSetRequestNewNameNil") + } + } + + checkUpdateSetRequestCurrentDescriptionNil := func() checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Nil(t, got.CurrentSet.Description, "checkUpdateSetRequestCurrentDescriptionNil") + } + } + + checkUpdateSetRequestNewDescription := func(want string) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Equal(t, wrapperspb.String(want), got.NewSet.Description, "checkUpdateSetRequestNewDescription") + } + } + + checkUpdateSetRequestNewDescriptionNil := func() checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Nil(t, got.NewSet.Description, "checkUpdateSetRequestNewDescriptionNil") + } + } + + checkUpdateSetRequestCurrentPreferredEndpoints := func(want []string) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Equal(t, want, got.CurrentSet.PreferredEndpoints, "checkUpdateSetRequestCurrentPreferredEndpoints") + } + } + + checkUpdateSetRequestNewPreferredEndpoints := func(want []string) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Equal(t, want, got.NewSet.PreferredEndpoints, "checkUpdateSetRequestNewPreferredEndpoints") + } + } + + checkUpdateSetRequestNewPreferredEndpointsNil := func() checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Nil(t, got.NewSet.PreferredEndpoints, "checkUpdateSetRequestNewPreferredEndpointsNil") + } + } + + checkUpdateSetRequestCurrentAttributes := func(want map[string]interface{}) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Empty(t, cmp.Diff(mustStruct(want), got.CurrentSet.Attributes, protocmp.Transform()), "checkUpdateSetRequestCurrentAttributes") + } + } + + checkUpdateSetRequestNewAttributes := func(want map[string]interface{}) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Empty(t, cmp.Diff(mustStruct(want), got.NewSet.Attributes, protocmp.Transform()), "checkUpdateSetRequestNewAttributes") + } + } + + checkUpdateSetRequestPersistedSecrets := func(want map[string]interface{}) checkPluginReqFunc { + return func(t *testing.T, got *plgpb.OnUpdateSetRequest) { + t.Helper() + assert.Empty(t, cmp.Diff(mustStruct(want), got.Persisted.Secrets, protocmp.Transform()), "checkUpdateSetRequestPersistedSecrets") + } + } + tests := []struct { name string withScopeId *string @@ -709,9 +661,10 @@ func TestRepository_UpdateSet(t *testing.T) { withPluginError error changeFuncs []changeHostSetFunc version uint32 - fieldMask []string - wantCheckFuncs []checkFunc - wantIsErr errors.Code + fieldMask []string + wantCheckSetFuncs []checkHostSetFunc + wantCheckPluginReqFuncs []checkPluginReqFunc + wantIsErr errors.Code }{ { name: "nil set", @@ -784,16 +737,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changeName("foo")}, version: 2, fieldMask: []string{"name"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentNameNil(), checkUpdateSetRequestNewName("foo"), - checkName("foo"), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkName("foo"), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -802,16 +756,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changeName("")}, version: 2, fieldMask: []string{"name"}, - wantCheckFuncs: []checkFunc{ - checkVersion(2), // Version remains same even though row is updated + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentNameNil(), checkUpdateSetRequestNewNameNil(), - checkName(""), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(2), // Version remains same even though row is updated + checkName(""), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -820,16 +775,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changeDescription("foo")}, version: 2, fieldMask: []string{"description"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentDescriptionNil(), checkUpdateSetRequestNewDescription("foo"), - checkDescription("foo"), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkDescription("foo"), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -838,16 +794,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changeDescription("")}, version: 2, fieldMask: []string{"description"}, - wantCheckFuncs: []checkFunc{ - checkVersion(2), // Version remains same even though row is updated + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentDescriptionNil(), checkUpdateSetRequestNewDescriptionNil(), - checkDescription(""), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(2), // Version remains same even though row is updated + checkDescription(""), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -856,16 +813,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})}, version: 2, fieldMask: []string{"PreferredEndpoints"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -874,16 +832,17 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changePreferredEndpoints(nil)}, version: 2, fieldMask: []string{"PreferredEndpoints"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), checkUpdateSetRequestNewPreferredEndpointsNil(), - checkPreferredEndpoints(nil), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkPreferredEndpoints(nil), checkNeedSync(false), - checkNumUpdated(1), }, }, { @@ -893,8 +852,7 @@ func TestRepository_UpdateSet(t *testing.T) { })}, version: 2, fieldMask: []string{"attributes"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ "foo": "bar", }), @@ -902,15 +860,17 @@ func TestRepository_UpdateSet(t *testing.T) { "foo": "bar", "baz": "qux", }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), checkAttributes(map[string]interface{}{ "foo": "bar", "baz": "qux", }), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), checkNeedSync(true), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -921,22 +881,23 @@ func TestRepository_UpdateSet(t *testing.T) { })}, version: 2, fieldMask: []string{"attributes"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ "foo": "bar", }), checkUpdateSetRequestNewAttributes(map[string]interface{}{ "foo": "baz", }), - checkAttributes(map[string]interface{}{ - "foo": "baz", - }), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{ + "foo": "baz", + }), checkNeedSync(true), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -947,18 +908,19 @@ func TestRepository_UpdateSet(t *testing.T) { })}, version: 2, fieldMask: []string{"attributes"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ "foo": "bar", }), checkUpdateSetRequestNewAttributes(map[string]interface{}{}), - checkAttributes(map[string]interface{}{}), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{}), checkNeedSync(true), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -967,18 +929,19 @@ func TestRepository_UpdateSet(t *testing.T) { changeFuncs: []changeHostSetFunc{changeAttributesNil()}, version: 2, fieldMask: []string{"attributes"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ "foo": "bar", }), checkUpdateSetRequestNewAttributes(map[string]interface{}{}), - checkAttributes(map[string]interface{}{}), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{}), checkNeedSync(true), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -990,8 +953,7 @@ func TestRepository_UpdateSet(t *testing.T) { })}, version: 2, fieldMask: []string{"attributes.a", "attributes.foo"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ "foo": "bar", }), @@ -999,15 +961,17 @@ func TestRepository_UpdateSet(t *testing.T) { "a": "b", "foo": "baz", }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), checkAttributes(map[string]interface{}{ "a": "b", "foo": "baz", }), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), checkNeedSync(true), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -1019,19 +983,20 @@ func TestRepository_UpdateSet(t *testing.T) { }, version: 2, fieldMask: []string{"name", "PreferredEndpoints"}, - wantCheckFuncs: []checkFunc{ - checkVersion(3), + wantCheckPluginReqFuncs: []checkPluginReqFunc{ checkUpdateSetRequestCurrentNameNil(), checkUpdateSetRequestNewName("foo"), - checkName("foo"), checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ "one": "two", }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkName("foo"), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), checkNeedSync(false), - checkNumUpdated(1), checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), }, }, @@ -1056,7 +1021,7 @@ func TestRepository_UpdateSet(t *testing.T) { dbKmsCache, sched, testCatalog, - testPluginMap, + dummyPluginMap, WithPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), ) // Set some (default) attributes on our test set @@ -1091,14 +1056,34 @@ func TestRepository_UpdateSet(t *testing.T) { t.Run(tt.name, func(t *testing.T) { require := require.New(t) assert := assert.New(t) + + // Define a plugin "manager", basically just a map with a mock + // plugin in it. This also includes functionality to capture the + // state, and set an error and the returned secrets to nil. Note + // that the way that this is set up means that the tests cannot run + // in parallel, but there could be other factors affecting that as + // well. + var gotOnUpdateCallCount int + testPluginMap := map[string]plgpb.HostPluginServiceClient{ + testPlugin.GetPublicId(): &WrappingPluginClient{ + Server: &TestPluginServer{ + OnUpdateSetFn: func(_ context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) { + gotOnUpdateCallCount++ + for _, check := range tt.wantCheckPluginReqFuncs { + check(t, req) + } + return &plgpb.OnUpdateSetResponse{}, tt.withPluginError + }, + }, + }, + } + origSet := setupHostSet(t, ctx) pluginMap := testPluginMap if tt.withEmptyPluginMap { pluginMap = make(map[string]plgpb.HostPluginServiceClient) } - pluginError = tt.withPluginError - t.Cleanup(func() { pluginError = nil }) repo, err := NewRepository(dbRW, dbRW, dbKmsCache, sched, pluginMap) require.NoError(err) require.NotNil(repo) @@ -1113,9 +1098,7 @@ func TestRepository_UpdateSet(t *testing.T) { scopeId = *tt.withScopeId } - var gotHosts []*Host - var gotPlugin *hostplugin.Plugin - gotSet, gotHosts, gotPlugin, gotNumUpdated, err = repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask) + gotUpdatedSet, gotHosts, gotPlugin, gotNumUpdated, err := repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask) t.Cleanup(func() { gotOnUpdateCallCount = 0 }) if tt.wantIsErr != 0 { require.Equal(db.NoRowsAffected, gotNumUpdated) @@ -1124,14 +1107,14 @@ func TestRepository_UpdateSet(t *testing.T) { } require.NoError(err) require.Equal(1, gotOnUpdateCallCount) - t.Cleanup(func() { gotOnUpdateSetRequest = nil }) + assert.Equal(1, gotNumUpdated) // Quick assertion that the set is not nil and that the plugin // ID in the catalog referenced by the set matches the plugin // ID in the returned plugin. - require.NotNil(gotSet) + require.NotNil(gotUpdatedSet) require.NotNil(gotPlugin) - assert.Equal(testCatalog.PublicId, gotSet.CatalogId) + assert.Equal(testCatalog.PublicId, gotUpdatedSet.CatalogId) assert.Equal(testCatalog.PluginId, gotPlugin.PublicId) // Also assert that the hosts returned by the request are the ones that belong to the set @@ -1146,9 +1129,12 @@ func TestRepository_UpdateSet(t *testing.T) { assert.Equal(wantHostMap, gotHostMap) // Perform checks - for _, check := range tt.wantCheckFuncs { - check(t, ctx) + for _, check := range tt.wantCheckSetFuncs { + check(t, gotUpdatedSet) } + + gotLookupSet, gotPlugin, err := repo.LookupSet(ctx, workingSet.GetPublicId()) + assert.Empty(cmp.Diff(gotUpdatedSet, gotLookupSet, protocmp.Transform())) }) } } diff --git a/internal/host/preferred_endpoint.go b/internal/host/preferred_endpoint.go index 9dc32dd5f..621448d85 100644 --- a/internal/host/preferred_endpoint.go +++ b/internal/host/preferred_endpoint.go @@ -59,8 +59,8 @@ func (pe *PreferredEndpoint) validate(ctx context.Context, caller errors.Op) err } // AllocPreferredEndpoint makes an empty one in-memory -func AllocPreferredEndpoint() PreferredEndpoint { - return PreferredEndpoint{ +func AllocPreferredEndpoint() *PreferredEndpoint { + return &PreferredEndpoint{ PreferredEndpoint: &store.PreferredEndpoint{}, } } diff --git a/internal/host/preferred_endpoint_test.go b/internal/host/preferred_endpoint_test.go index 47151564f..a4a6a6ff9 100644 --- a/internal/host/preferred_endpoint_test.go +++ b/internal/host/preferred_endpoint_test.go @@ -61,7 +61,7 @@ func TestPreferredEndpoint_Create(t *testing.T) { want.HostSetId = set.PublicId want.Condition = "cidr:1.2.3.4" want.Priority = 1 - return &want + return want }(), }, { @@ -77,7 +77,7 @@ func TestPreferredEndpoint_Create(t *testing.T) { want.HostSetId = set.PublicId want.Condition = "cidr:1.2.3.4" want.Priority = 1 - return &want + return want }(), wantCreateErr: true, wantCreateIsErr: errors.NotUnique, @@ -141,8 +141,8 @@ func TestPreferredEndpoint_Create(t *testing.T) { assert.NoError(err) } found := host.AllocPreferredEndpoint() - require.NoError(rw.LookupWhere(ctx, &found, "host_set_id = ? and priority = ?", tt.args.hostSetId, tt.args.priority)) - assert.Equal(got, &found) + require.NoError(rw.LookupWhere(ctx, found, "host_set_id = ? and priority = ?", tt.args.hostSetId, tt.args.priority)) + assert.Equal(got, found) } }) } From 61f9d1f6af92b897add35593130ca4f56ca4601b Mon Sep 17 00:00:00 2001 From: Todd Date: Thu, 11 Nov 2021 14:32:15 -0700 Subject: [PATCH 67/71] Add update set handler and sdk functionality (#1684) --- internal/gen/controller.swagger.json | 2 +- .../plugin/repository_host_catalog_test.go | 14 +- internal/host/plugin/store/host.pb.go | 131 +++---- internal/host/static/store/static.pb.go | 42 ++- .../api/resources/hostsets/v1/host_set.proto | 3 +- .../storage/host/plugin/store/v1/host.proto | 2 +- .../storage/host/static/store/v1/static.proto | 1 + .../host_catalog_service_test.go | 46 +-- .../handlers/host_sets/host_set_service.go | 53 ++- .../host_sets/host_set_service_test.go | 319 +++++++++++++++++- internal/tests/api/hostsets/host_set_test.go | 11 +- .../api/resources/hostsets/host_set.pb.go | 54 +-- 12 files changed, 535 insertions(+), 143 deletions(-) diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 983b3a9a7..8723b2ace 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -4322,7 +4322,7 @@ "items": { "type": "string" }, - "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host. Preferences are specified by\n\"cidr:\u003cvalid IPv4/6 CIDR\u003e\" or \"dns:\u003cglobbed name\u003e\", specifying which IP\naddress or DNS name out of a host's available possibilities should be\npreferred. If no preferences are specified, a value will be chosen from\namong all avialable values using a built-in priority order. May not be\nvalid for all plugin types." + "description": "multiple possible endpoints for a host. Preferences are specified by\n\"cidr:\u003cvalid IPv4/6 CIDR\u003e\" or \"dns:\u003cglobbed name\u003e\", specifying which IP\naddress or DNS name out of a host's available possibilities should be\npreferred. If no preferences are specified, a value will be chosen from\namong all avialable values using a built-in priority order. May not be\nvalid for all plugin types." }, "sync_interval_seconds": { "type": "integer", diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index ac69d403d..9eb7fb665 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -37,13 +37,13 @@ func TestRepository_CreateCatalog(t *testing.T) { unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented") tests := []struct { - name string - in *HostCatalog - opts []Option - want *HostCatalog + name string + in *HostCatalog + opts []Option + want *HostCatalog wantPluginCalled bool - wantSecret *structpb.Struct - wantIsErr errors.Code + wantSecret *structpb.Struct + wantIsErr errors.Code }{ { name: "nil-catalog", @@ -179,7 +179,7 @@ func TestRepository_CreateCatalog(t *testing.T) { PluginId: plg.GetPublicId(), Attributes: func() []byte { st, err := structpb.NewStruct(map[string]interface{}{ - "k1": "foo", + "k1": "foo", "nilkey": nil, }) require.NoError(t, err) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 3100beb71..0fa003b26 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -669,7 +669,7 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x87, 0x05, 0x0a, 0x07, 0x48, 0x6f, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb6, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -700,70 +700,73 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5e, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x64, 0x0a, - 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, - 0x29, 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, - 0x73, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, - 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, - 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, - 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, - 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, - 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, - 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, - 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x42, 0x2d, 0xc2, 0xdd, 0x29, 0x29, 0x0a, + 0x12, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x64, 0x0a, 0x15, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, 0x29, + 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, + 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, + 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, + 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, + 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, + 0x0d, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, + 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, + 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/host/static/store/static.pb.go b/internal/host/static/store/static.pb.go index aad9a2418..f0ed54bb3 100644 --- a/internal/host/static/store/static.pb.go +++ b/internal/host/static/store/static.pb.go @@ -441,7 +441,8 @@ type UnimplementedSetFields struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SyncIntervalSeconds int32 `protobuf:"varint,22,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty"` + PreferredEndpoints []string `protobuf:"bytes,11,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty"` + SyncIntervalSeconds int32 `protobuf:"varint,22,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty"` } func (x *UnimplementedSetFields) Reset() { @@ -476,6 +477,13 @@ func (*UnimplementedSetFields) Descriptor() ([]byte, []int) { return file_controller_storage_host_static_store_v1_static_proto_rawDescGZIP(), []int{4} } +func (x *UnimplementedSetFields) GetPreferredEndpoints() []string { + if x != nil { + return x.PreferredEndpoints + } + return nil +} + func (x *UnimplementedSetFields) GetSyncIntervalSeconds() int32 { if x != nil { return x.SyncIntervalSeconds @@ -574,19 +582,25 @@ var file_controller_storage_host_static_store_v1_static_proto_rawDesc = []byte{ 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x22, 0x7e, 0x0a, 0x16, 0x55, 0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x64, 0x0a, 0x15, - 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, 0x29, - 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, - 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, - 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x49, 0x64, 0x22, 0xde, 0x01, 0x0a, 0x16, 0x55, 0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x5e, 0x0a, + 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x42, 0x2d, 0xc2, 0xdd, 0x29, 0x29, + 0x0a, 0x12, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x12, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x64, 0x0a, + 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, + 0x29, 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, + 0x73, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, + 0x73, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index 126f73392..d5baf1535 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -47,14 +47,13 @@ message HostSet { // Output only. A list of Hosts in this Host Set. repeated string host_ids = 100 [json_name="host_ids"]; - // An ordered list of endpoint preferences used to choose from among // multiple possible endpoints for a host. Preferences are specified by // "cidr:" or "dns:", specifying which IP // address or DNS name out of a host's available possibilities should be // preferred. If no preferences are specified, a value will be chosen from // among all avialable values using a built-in priority order. May not be // valid for all plugin types. - repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true]; + repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"preferred_endpoints" that: "PreferredEndpoints"}]; // An interger number of seconds indicating the amount of time that should // elapse between syncs of the host set. The interval will be applied to the diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 5fa3c8337..fa40064aa 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -92,7 +92,7 @@ message HostSet { // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` - repeated string preferred_endpoints = 11; + repeated string preferred_endpoints = 11 [(custom_options.v1.mask_mapping) = {this:"PreferredEndpoints" that: "preferred_endpoints"}]; // Sync interval is a value representing a duration in seconds // @inject_tag: `gorm:"default:null"` diff --git a/internal/proto/local/controller/storage/host/static/store/v1/static.proto b/internal/proto/local/controller/storage/host/static/store/v1/static.proto index daf3849da..8eea0f87e 100644 --- a/internal/proto/local/controller/storage/host/static/store/v1/static.proto +++ b/internal/proto/local/controller/storage/host/static/store/v1/static.proto @@ -122,5 +122,6 @@ message HostSetMember { // here for the purpose of identifying the mask maps which are on the top level // api set resource but aren't present in the static host set storage. message UnimplementedSetFields { + repeated string preferred_endpoints = 11 [(custom_options.v1.mask_mapping) = {this:"PreferredEndpoints" that: "preferred_endpoints"}]; int32 sync_interval_seconds = 22 [(custom_options.v1.mask_mapping) = {this: "SyncIntervalSeconds" that: "sync_interval_seconds"}]; } \ No newline at end of file diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index dde3262e0..515de1058 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -1380,12 +1380,12 @@ func TestUpdate_Plugin(t *testing.T) { }) require.NoError(t, err) resp, err := tested.CreateHostCatalog(ctx, &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ - ScopeId: proj.GetPublicId(), - PluginId: plg.GetPublicId(), - Name: wrapperspb.String("default"), - Description: wrapperspb.String("default"), - Type: "plugin", - Attributes: attr, + ScopeId: proj.GetPublicId(), + PluginId: plg.GetPublicId(), + Name: wrapperspb.String("default"), + Description: wrapperspb.String("default"), + Type: "plugin", + Attributes: attr, }}) require.NoError(t, err) id := resp.GetItem().GetId() @@ -1424,14 +1424,14 @@ func TestUpdate_Plugin(t *testing.T) { } cases := []struct { - name string - masks []string + name string + masks []string changes []updateFn - check func(*testing.T, *pb.HostCatalog) - err error + check func(*testing.T, *pb.HostCatalog) + err error }{ { - name: "Update an Existing HostCatalog", + name: "Update an Existing HostCatalog", masks: []string{"name", "description"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1444,7 +1444,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Update arbitrary attribute for catalog", + name: "Update arbitrary attribute for catalog", masks: []string{"attributes.newkey", "attributes.foo"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1462,7 +1462,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Multiple Paths in single string", + name: "Multiple Paths in single string", masks: []string{"name,description"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1484,7 +1484,7 @@ func TestUpdate_Plugin(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Empty Path", + name: "Empty Path", masks: []string{}, changes: []updateFn{ clearReadOnlyFields(), @@ -1494,7 +1494,7 @@ func TestUpdate_Plugin(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Only non-existant paths in Mask", + name: "Only non-existant paths in Mask", masks: []string{"nonexistant_field"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1504,7 +1504,7 @@ func TestUpdate_Plugin(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Unset Name", + name: "Unset Name", masks: []string{"name"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1517,7 +1517,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Unset Description", + name: "Unset Description", masks: []string{"description"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1530,7 +1530,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Update Only Name", + name: "Update Only Name", masks: []string{"name"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1543,7 +1543,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Update Only Description", + name: "Update Only Description", masks: []string{"description"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1556,7 +1556,7 @@ func TestUpdate_Plugin(t *testing.T) { }, }, { - name: "Cant change type", + name: "Cant change type", masks: []string{"type"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1567,7 +1567,7 @@ func TestUpdate_Plugin(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Cant specify Updated Time", + name: "Cant specify Updated Time", masks: []string{"updated_time"}, changes: []updateFn{ clearReadOnlyFields(), @@ -1589,8 +1589,8 @@ func TestUpdate_Plugin(t *testing.T) { } req := &pbs.UpdateHostCatalogRequest{ - Id: id, - Item: hc, + Id: id, + Item: hc, UpdateMask: &field_mask.FieldMask{Paths: tc.masks}, } diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 36acd7c73..9c47f7e65 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -256,7 +256,7 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques if authResults.Error != nil { return nil, authResults.Error } - hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req) + hs, hosts, plg, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req) if err != nil { return nil, err } @@ -268,6 +268,9 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques outputOpts := make([]handlers.Option, 0, 3) outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if plg != nil { + outputOpts = append(outputOpts, handlers.WithPlugin(plg)) + } if outputFields.Has(globals.ScopeField) { outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) } @@ -512,8 +515,8 @@ func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, it return hSet, plg, nil } -func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, error) { - const op = "host_sets.(Service).updateInRepo" +func (s Service) updateStaticInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, error) { + const op = "host_sets.(Service).updateStaticInRepo" item := req.GetItem() h, err := toStorageStaticSet(ctx, catalogId, item) if err != nil { @@ -542,6 +545,47 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, re return out, hl, nil } +func (s Service) updatePluginInRepo(ctx context.Context, scopeId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, *plugins.PluginInfo, error) { + const op = "host_sets.(Service).updatePluginInRepo" + item := req.GetItem() + h, err := toStoragePluginSet(ctx, "", item) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update")) + } + h.PublicId = req.GetId() + dbMask := maskManager[plugin.Subtype].Translate(req.GetUpdateMask().GetPaths(), "attributes.") + if len(dbMask) == 0 { + return nil, nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) + } + repo, err := s.pluginRepoFn() + if err != nil { + return nil, nil, nil, err + } + out, hosts, plg, rowsUpdated, err := repo.UpdateSet(ctx, scopeId, h, item.GetVersion(), dbMask) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update host set")) + } + if rowsUpdated == 0 { + return nil, nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", req.GetId()) + } + var hl []host.Host + for _, h := range hosts { + hl = append(hl, h) + } + return out, hl, toPluginInfo(plg), nil +} + +func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (hs host.Set, hosts []host.Host, plg *plugins.PluginInfo, err error) { + const op = "host_sets.(Service).updateInRepo" + switch host.SubtypeFromId(req.GetId()) { + case static.Subtype: + hs, hosts, err = s.updateStaticInRepo(ctx, scopeId, catalogId, req) + case plugin.Subtype: + hs, hosts, plg, err = s.updatePluginInRepo(ctx, scopeId, req) + } + return +} + func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) { const op = "host_sets.(Service).deleteFromRepo" rows := 0 @@ -909,6 +953,9 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) e if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { badFields[globals.TypeField] = "Doesn't match the parent resource's type." } + if len(req.GetItem().PreferredEndpoints) > 0 { + badFields[globals.PreferredEndpointsField] = "This field is not yet supported for static host sets." + } case plugin.Subtype: if req.GetItem().GetType() != "" && req.GetItem().GetType() != plugin.Subtype.String() { badFields[globals.TypeField] = "Doesn't match the parent resource's type." diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index 5d0c7ae4d..0f8b0c82a 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -665,6 +665,7 @@ func TestCreate_Static(t *testing.T) { return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] + prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"} defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime() @@ -694,6 +695,17 @@ func TestCreate_Static(t *testing.T) { }, }, }, + { + name: "With Preferred Endpoints", + req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "name"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: static.Subtype.String(), + PreferredEndpoints: prefEndpoints, + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, { name: "Create with unknown type", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ @@ -1050,7 +1062,7 @@ func TestCreate_Plugin(t *testing.T) { } } -func TestUpdate(t *testing.T) { +func TestUpdate_Static(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -1431,6 +1443,311 @@ func TestUpdate(t *testing.T) { } } +func TestUpdate_Plugin(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + + name := "test" + plg := hostplugin.TestPlugin(t, conn, name) + plgm := map[string]plgpb.HostPluginServiceClient{ + plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + } + + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + pluginHostRepo := func() (*plugin.Repository, error) { + return plugin.NewRepository(rw, rw, kms, sche, plgm) + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + tested, err := host_sets.NewService(repoFn, pluginHostRepo) + require.NoError(t, err, "Failed to create a new host catalog service.") + + hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) + ctx := auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()) + + freshSet := func(t *testing.T) *pb.HostSet { + attr, err := structpb.NewStruct(map[string]interface{}{ + "foo": "bar", + }) + require.NoError(t, err) + resp, err := tested.CreateHostSet(ctx, &pbs.CreateHostSetRequest{Item: &pb.HostSet{ + HostCatalogId: hc.GetPublicId(), + Name: wrapperspb.String("default"), + Description: wrapperspb.String("default"), + Type: plugin.Subtype.String(), + Attributes: attr, + PreferredEndpoints: []string{"dns:default"}, + }}) + require.NoError(t, err) + id := resp.GetItem().GetId() + t.Cleanup(func() { + _, err := tested.DeleteHostSet(ctx, &pbs.DeleteHostSetRequest{Id: id}) + require.NoError(t, err) + }) + return resp.GetItem() + } + + type updateFn func(catalog *pb.HostSet) + clearReadOnlyFields := func() updateFn { + return func(c *pb.HostSet) { + c.Id = "" + c.Plugin = nil + c.AuthorizedActions = nil + c.CreatedTime = nil + c.UpdatedTime = nil + } + } + updateName := func(i *wrappers.StringValue) updateFn { + return func(c *pb.HostSet) { + c.Name = i + } + } + updateDesc := func(i *wrappers.StringValue) updateFn { + return func(c *pb.HostSet) { + c.Description = i + } + } + updatePreferedEndpoints := func(i []string) updateFn { + return func(c *pb.HostSet) { + c.PreferredEndpoints = i + } + } + updateAttrs := func(i *structpb.Struct) updateFn { + return func(c *pb.HostSet) { + c.Attributes = i + } + } + + cases := []struct { + name string + masks []string + changes []updateFn + check func(*testing.T, *pb.HostSet) + err error + }{ + { + name: "Update an existing resource", + masks: []string{"name", "description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, "new", in.Name.GetValue()) + assert.Equal(t, "desc", in.Description.GetValue()) + }, + }, + { + name: "Update arbitrary attribute", + masks: []string{"attributes.newkey", "attributes.foo"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateAttrs(func() *structpb.Struct { + attr, err := structpb.NewStruct(map[string]interface{}{ + "newkey": "newvalue", + "foo": nil, + }) + require.NoError(t, err) + return attr + }()), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, map[string]interface{}{"newkey": "newvalue"}, in.GetAttributes().AsMap()) + }, + }, + { + name: "Update preferred endpoints", + masks: []string{"preferred_endpoints"}, + changes: []updateFn{ + clearReadOnlyFields(), + updatePreferedEndpoints([]string{"dns:new"}), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, in.PreferredEndpoints, []string{"dns:new"}) + }, + }, + { + name: "Don't update preferred_endpoints", + masks: []string{"name"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updatePreferedEndpoints([]string{"dns:ignored"}), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, "new", in.Name.GetValue()) + assert.Equal(t, in.PreferredEndpoints, []string{"dns:default"}) + }, + }, + { + name: "Multiple Paths in single string", + masks: []string{"name,description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, "new", in.Name.GetValue()) + assert.Equal(t, "desc", in.Description.GetValue()) + }, + }, + { + name: "No Update Mask", + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Empty Path", + masks: []string{}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Only non-existant paths in Mask", + masks: []string{"nonexistant_field"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("new")), + updateDesc(wrapperspb.String("desc")), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Unset Name", + masks: []string{"name"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(nil), + updateDesc(wrapperspb.String("ignored")), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Nil(t, in.Name) + assert.Equal(t, "default", in.Description.GetValue()) + }, + }, + { + name: "Unset Description", + masks: []string{"description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("ignored")), + updateDesc(nil), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Nil(t, in.Description) + assert.Equal(t, "default", in.Name.GetValue()) + }, + }, + { + name: "Update Only Name", + masks: []string{"name"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("updated")), + updateDesc(wrapperspb.String("ignored")), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, "updated", in.Name.GetValue()) + assert.Equal(t, "default", in.Description.GetValue()) + }, + }, + { + name: "Update Only Description", + masks: []string{"description"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateName(wrapperspb.String("ignored")), + updateDesc(wrapperspb.String("updated")), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, "default", in.Name.GetValue()) + assert.Equal(t, "updated", in.Description.GetValue()) + }, + }, + { + name: "Cant change type", + masks: []string{"type"}, + changes: []updateFn{ + clearReadOnlyFields(), + func(c *pb.HostSet) { + c.Type = "static" + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify Updated Time", + masks: []string{"updated_time"}, + changes: []updateFn{ + clearReadOnlyFields(), + func(c *pb.HostSet) { + c.UpdatedTime = timestamppb.Now() + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + hc := freshSet(t) + + id := hc.GetId() + for _, f := range tc.changes { + f(hc) + } + + req := &pbs.UpdateHostSetRequest{ + Id: id, + Item: hc, + UpdateMask: &field_mask.FieldMask{Paths: tc.masks}, + } + + // Test some bad versions + req.Item.Version = 2 + _, gErr := tested.UpdateHostSet(ctx, req) + require.Error(gErr) + req.Item.Version = 0 + _, gErr = tested.UpdateHostSet(ctx, req) + require.Error(gErr) + req.Item.Version = 1 + + got, gErr := tested.UpdateHostSet(ctx, req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "UpdateHostSet(%+v) got error %v, wanted %v", req, gErr, tc.err) + return + } + require.NoError(gErr) + require.NotNil(got) + + item := got.GetItem() + assert.Equal(uint32(2), item.GetVersion()) + assert.Greater(item.GetUpdatedTime().AsTime().UnixNano(), item.GetCreatedTime().AsTime().UnixNano()) + tc.check(t, item) + }) + } +} + func TestAddHostSetHosts(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) diff --git a/internal/tests/api/hostsets/host_set_test.go b/internal/tests/api/hostsets/host_set_test.go index 3316e09f3..dcfbdbb8c 100644 --- a/internal/tests/api/hostsets/host_set_test.go +++ b/internal/tests/api/hostsets/host_set_test.go @@ -222,12 +222,21 @@ func TestCrud(t *testing.T) { hostcatalogs.WithAttributes(map[string]interface{}{"foo": "bar"})) require.NoError(err) - h, err = hClient.Create(tc.Context(), c.Item.Id, hostsets.WithName("foo")) + h, err = hClient.Create(tc.Context(), c.Item.Id, hostsets.WithName("foo"), + hostsets.WithAttributes(map[string]interface{}{"foo": "bar"}), hostsets.WithPreferredEndpoints([]string{"dns:test"})) checkHost(t, "create", h.Item, err, "foo", 1) h, err = hClient.Read(tc.Context(), h.Item.Id) checkHost(t, "read", h.Item, err, "foo", 1) + h, err = hClient.Update(tc.Context(), h.Item.Id, h.Item.Version, hostsets.WithName("bar"), + hostsets.WithAttributes(map[string]interface{}{"foo": nil, "key": "val"}), + hostsets.WithPreferredEndpoints([]string{"dns:update"})) + checkHost(t, "update", h.Item, err, "bar", 2) + + assert.Equal(h.Item.Attributes, map[string]interface{}{"key": "val"}) + assert.Equal(h.Item.PreferredEndpoints, []string{"dns:update"}) + _, err = hClient.Delete(tc.Context(), h.Item.Id) assert.NoError(err) _, err = hClient.Delete(tc.Context(), h.Item.Id) diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index 6c883dede..f87ceffde 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -55,7 +55,6 @@ type HostSet struct { Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty"` // Output only. A list of Hosts in this Host Set. HostIds []string `protobuf:"bytes,100,rep,name=host_ids,proto3" json:"host_ids,omitempty"` - // An ordered list of endpoint preferences used to choose from among // multiple possible endpoints for a host. Preferences are specified by // "cidr:" or "dns:", specifying which IP // address or DNS name out of a host's available possibilities should be @@ -236,7 +235,7 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x06, 0x0a, 0x07, 0x48, 0x6f, 0x73, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa6, 0x07, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, @@ -273,31 +272,34 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, + 0x64, 0x73, 0x12, 0x63, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x09, 0x42, - 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x73, - 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, - 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x34, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, - 0x2c, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x15, 0x73, - 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x18, 0x6e, 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, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, - 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x29, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x12, + 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x42, 0x34, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2c, 0x0a, 0x15, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x15, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, + 0x6e, 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, 0x42, 0x04, + 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, + 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From f41bceda83457a8220a016fd55a465f63858ab50 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Fri, 12 Nov 2021 08:57:10 -0700 Subject: [PATCH 68/71] Merge from main and `make gen` --- internal/db/read_writer.go | 4 ++-- internal/host/plugin/repository_host_set.go | 2 +- .../host/plugin/repository_host_set_test.go | 18 +++++++++--------- internal/servers/controller/interceptor.go | 1 - 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 37c8592cb..338339a68 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -621,8 +621,8 @@ func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string // Delete an object in the db with options: WithOplog, NewOplogMsg, WithWhere. // WithOplog will write an oplog entry for the delete. NewOplogMsg will return // in-memory oplog message. WithOplog and NewOplogMsg cannot be used together. -//WithWhere allows specifying an additional constraint on the operation in -//addition to the PKs. Delete returns the number of rows deleted and any errors. +// WithWhere allows specifying an additional constraint on the operation in +// addition to the PKs. Delete returns the number of rows deleted and any errors. func (rw *Db) Delete(ctx context.Context, i interface{}, opt ...Option) (int, error) { const op = "db.Delete" if rw.underlying == nil { diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 40988766e..69f3aef04 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -359,7 +359,7 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { returnedSet = newSet.clone() - msgs := make([]*oplog.Message, 0, len(preferredEndpoints) + len(currentSet.PreferredEndpoints) + 2) + msgs := make([]*oplog.Message, 0, len(preferredEndpoints)+len(currentSet.PreferredEndpoints)+2) ticket, err := w.GetTicket(s) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 6c1ce6302..62206535a 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -655,16 +655,16 @@ func TestRepository_UpdateSet(t *testing.T) { } tests := []struct { - name string - withScopeId *string - withEmptyPluginMap bool - withPluginError error - changeFuncs []changeHostSetFunc - version uint32 - fieldMask []string - wantCheckSetFuncs []checkHostSetFunc + name string + withScopeId *string + withEmptyPluginMap bool + withPluginError error + changeFuncs []changeHostSetFunc + version uint32 + fieldMask []string + wantCheckSetFuncs []checkHostSetFunc wantCheckPluginReqFuncs []checkPluginReqFunc - wantIsErr errors.Code + wantIsErr errors.Code }{ { name: "nil set", diff --git a/internal/servers/controller/interceptor.go b/internal/servers/controller/interceptor.go index 2f741f8b1..5eec995fb 100644 --- a/internal/servers/controller/interceptor.go +++ b/internal/servers/controller/interceptor.go @@ -274,7 +274,6 @@ func workerRequestInfoInterceptor(ctx context.Context, eventer *event.Eventer) ( const op = "worker.requestInfoInterceptor" if eventer == nil { return nil, errors.New(ctx, errors.InvalidParameter, op, "missing eventer") - } return func(interceptorCtx context.Context, req interface{}, From 5cfcc41f00387dc30e5814d79090fc8e4fd8d00e Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 12 Nov 2021 09:04:48 -0700 Subject: [PATCH 69/71] Fix bug for adding preferred endpoint when non existed prior. (#1692) --- internal/host/plugin/repository_host_set.go | 34 +- .../host/plugin/repository_host_set_test.go | 760 ++++++++++-------- 2 files changed, 426 insertions(+), 368 deletions(-) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index 69f3aef04..109f8755e 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -410,23 +410,25 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, } case endpointOpUpdate: - // Delete all old endpoint entries. - var peps []interface{} - for i := 1; i <= len(currentSet.PreferredEndpoints); i++ { - p := host.AllocPreferredEndpoint() - p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i) - peps = append(peps, p) - } - deleteOplogMsgs := make([]*oplog.Message, 0, len(peps)) - _, err := w.DeleteItems(ctx, peps, db.WithDebug(true), db.NewOplogMsgs(&deleteOplogMsgs)) - if err != nil { - return errors.Wrap(ctx, err, op) - } + if len(currentSet.PreferredEndpoints) > 0 { + // Delete all old endpoint entries. + var peps []interface{} + for i := 1; i <= len(currentSet.PreferredEndpoints); i++ { + p := host.AllocPreferredEndpoint() + p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i) + peps = append(peps, p) + } + deleteOplogMsgs := make([]*oplog.Message, 0, len(peps)) + _, err := w.DeleteItems(ctx, peps, db.WithDebug(true), db.NewOplogMsgs(&deleteOplogMsgs)) + if err != nil { + return errors.Wrap(ctx, err, op) + } - // Only append the oplog message if an operation was actually - // performed. - if len(deleteOplogMsgs) > 0 { - msgs = append(msgs, deleteOplogMsgs...) + // Only append the oplog message if an operation was actually + // performed. + if len(deleteOplogMsgs) > 0 { + msgs = append(msgs, deleteOplogMsgs...) + } } // Create the new entries. diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index 62206535a..5bda4438b 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -654,352 +654,28 @@ func TestRepository_UpdateSet(t *testing.T) { } } - tests := []struct { - name string - withScopeId *string - withEmptyPluginMap bool - withPluginError error - changeFuncs []changeHostSetFunc - version uint32 - fieldMask []string - wantCheckSetFuncs []checkHostSetFunc - wantCheckPluginReqFuncs []checkPluginReqFunc - wantIsErr errors.Code - }{ - { - name: "nil set", - changeFuncs: []changeHostSetFunc{changeSetToNil()}, - wantIsErr: errors.InvalidParameter, - }, - { - name: "nil embedded set", - changeFuncs: []changeHostSetFunc{changeEmbeddedSetToNil()}, - wantIsErr: errors.InvalidParameter, - }, - { - name: "missing public id", - changeFuncs: []changeHostSetFunc{changePublicId("")}, - wantIsErr: errors.InvalidParameter, - }, - { - name: "missing scope id", - withScopeId: func() *string { a := ""; return &a }(), - wantIsErr: errors.InvalidParameter, - }, - { - name: "empty field mask", - fieldMask: nil, // Should be testing on len - wantIsErr: errors.EmptyFieldMask, - }, - { - name: "bad set id", - changeFuncs: []changeHostSetFunc{changePublicId("badid")}, - fieldMask: []string{"name"}, - wantIsErr: errors.RecordNotFound, - }, - { - name: "version mismatch", - changeFuncs: []changeHostSetFunc{changeName("foo")}, - version: 1, - fieldMask: []string{"name"}, - wantIsErr: errors.VersionMismatch, - }, - { - name: "mismatched scope id to catalog scope", - withScopeId: func() *string { a := "badid"; return &a }(), - version: 2, - fieldMask: []string{"name"}, - wantIsErr: errors.InvalidParameter, - }, - { - name: "plugin lookup error", - withEmptyPluginMap: true, - version: 2, - fieldMask: []string{"name"}, - wantIsErr: errors.Internal, - }, - { - name: "plugin invocation error", - withPluginError: errors.New(context.Background(), errors.Internal, "TestRepository_UpdateSet/plugin_invocation_error", "test plugin error"), - version: 2, - fieldMask: []string{"name"}, - wantIsErr: errors.Internal, - }, - { - name: "update name (duplicate)", - changeFuncs: []changeHostSetFunc{changeName(testDuplicateSetName)}, - version: 2, - fieldMask: []string{"name"}, - wantIsErr: errors.NotUnique, - }, - { - name: "update name", - changeFuncs: []changeHostSetFunc{changeName("foo")}, - version: 2, - fieldMask: []string{"name"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentNameNil(), - checkUpdateSetRequestNewName("foo"), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkName("foo"), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update name to same", - changeFuncs: []changeHostSetFunc{changeName("")}, - version: 2, - fieldMask: []string{"name"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentNameNil(), - checkUpdateSetRequestNewNameNil(), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(2), // Version remains same even though row is updated - checkName(""), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update description", - changeFuncs: []changeHostSetFunc{changeDescription("foo")}, - version: 2, - fieldMask: []string{"description"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentDescriptionNil(), - checkUpdateSetRequestNewDescription("foo"), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkDescription("foo"), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update description to same", - changeFuncs: []changeHostSetFunc{changeDescription("")}, - version: 2, - fieldMask: []string{"description"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentDescriptionNil(), - checkUpdateSetRequestNewDescriptionNil(), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(2), // Version remains same even though row is updated - checkDescription(""), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update preferred endpoints", - changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})}, - version: 2, - fieldMask: []string{"PreferredEndpoints"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), - checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "clear preferred endpoints", - changeFuncs: []changeHostSetFunc{changePreferredEndpoints(nil)}, - version: 2, - fieldMask: []string{"PreferredEndpoints"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), - checkUpdateSetRequestNewPreferredEndpointsNil(), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkPreferredEndpoints(nil), - checkNeedSync(false), - }, - }, - { - name: "update attributes (add)", - changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ - "baz": "qux", - })}, - version: 2, - fieldMask: []string{"attributes"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ - "foo": "bar", - }), - checkUpdateSetRequestNewAttributes(map[string]interface{}{ - "foo": "bar", - "baz": "qux", - }), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkAttributes(map[string]interface{}{ - "foo": "bar", - "baz": "qux", - }), - checkNeedSync(true), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update attributes (overwrite)", - changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ - "foo": "baz", - })}, - version: 2, - fieldMask: []string{"attributes"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ - "foo": "bar", - }), - checkUpdateSetRequestNewAttributes(map[string]interface{}{ - "foo": "baz", - }), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkAttributes(map[string]interface{}{ - "foo": "baz", - }), - checkNeedSync(true), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update attributes (null)", - changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ - "foo": nil, - })}, - version: 2, - fieldMask: []string{"attributes"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ - "foo": "bar", - }), - checkUpdateSetRequestNewAttributes(map[string]interface{}{}), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkAttributes(map[string]interface{}{}), - checkNeedSync(true), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update attributes (full null)", - changeFuncs: []changeHostSetFunc{changeAttributesNil()}, - version: 2, - fieldMask: []string{"attributes"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ - "foo": "bar", - }), - checkUpdateSetRequestNewAttributes(map[string]interface{}{}), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkAttributes(map[string]interface{}{}), - checkNeedSync(true), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update attributes (combined)", - changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ - "a": "b", - "foo": "baz", - })}, - version: 2, - fieldMask: []string{"attributes.a", "attributes.foo"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ - "foo": "bar", - }), - checkUpdateSetRequestNewAttributes(map[string]interface{}{ - "a": "b", - "foo": "baz", - }), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkAttributes(map[string]interface{}{ - "a": "b", - "foo": "baz", - }), - checkNeedSync(true), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, - { - name: "update name and preferred endpoints", - changeFuncs: []changeHostSetFunc{ - changePreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - changeName("foo"), - }, - version: 2, - fieldMask: []string{"name", "PreferredEndpoints"}, - wantCheckPluginReqFuncs: []checkPluginReqFunc{ - checkUpdateSetRequestCurrentNameNil(), - checkUpdateSetRequestNewName("foo"), - checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), - checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ - "one": "two", - }), - }, - wantCheckSetFuncs: []checkHostSetFunc{ - checkVersion(3), - checkName("foo"), - checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), - checkNeedSync(false), - checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), - }, - }, + // Finally define a function for bringing the test subject host + // set. + setupBareHostSet := func(t *testing.T, ctx context.Context) (*HostSet, []*Host) { + t.Helper() + set := TestSet( + t, + dbConn, + dbKmsCache, + sched, + testCatalog, + dummyPluginMap, + ) + + t.Cleanup(func() { + t.Helper() + assert := assert.New(t) + n, err := dbRW.Delete(ctx, set) + assert.NoError(err) + assert.Equal(1, n) + }) + + return set, nil } // Create a host that will be verified as belonging to the created set below. @@ -1011,7 +687,7 @@ func TestRepository_UpdateSet(t *testing.T) { // Finally define a function for bringing the test subject host // set. - setupHostSet := func(t *testing.T, ctx context.Context) *HostSet { + setupHostSet := func(t *testing.T, ctx context.Context) (*HostSet, []*Host) { t.Helper() require := require.New(t) @@ -1048,7 +724,382 @@ func TestRepository_UpdateSet(t *testing.T) { assert.Equal(1, n) }) - return set + return set, testHosts + } + + tests := []struct { + name string + startingSet func(*testing.T, context.Context) (*HostSet, []*Host) + withScopeId *string + withEmptyPluginMap bool + withPluginError error + changeFuncs []changeHostSetFunc + version uint32 + fieldMask []string + wantCheckSetFuncs []checkHostSetFunc + wantCheckPluginReqFuncs []checkPluginReqFunc + wantIsErr errors.Code + }{ + { + name: "nil set", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changeSetToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil embedded set", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changeEmbeddedSetToNil()}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing public id", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changePublicId("")}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "missing scope id", + startingSet: setupBareHostSet, + withScopeId: func() *string { a := ""; return &a }(), + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty field mask", + startingSet: setupBareHostSet, + fieldMask: nil, // Should be testing on len + wantIsErr: errors.EmptyFieldMask, + }, + { + name: "bad set id", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changePublicId("badid")}, + fieldMask: []string{"name"}, + wantIsErr: errors.RecordNotFound, + }, + { + name: "version mismatch", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changeName("foo")}, + version: 4, + fieldMask: []string{"name"}, + wantIsErr: errors.VersionMismatch, + }, + { + name: "mismatched scope id to catalog scope", + startingSet: setupBareHostSet, + withScopeId: func() *string { a := "badid"; return &a }(), + fieldMask: []string{"name"}, + wantIsErr: errors.InvalidParameter, + }, + { + name: "plugin lookup error", + startingSet: setupBareHostSet, + withEmptyPluginMap: true, + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "plugin invocation error", + startingSet: setupBareHostSet, + withPluginError: errors.New(context.Background(), errors.Internal, "TestRepository_UpdateSet/plugin_invocation_error", "test plugin error"), + fieldMask: []string{"name"}, + wantIsErr: errors.Internal, + }, + { + name: "update name (duplicate)", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changeName(testDuplicateSetName)}, + fieldMask: []string{"name"}, + wantIsErr: errors.NotUnique, + }, + { + name: "update name", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeName("foo")}, + fieldMask: []string{"name"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewName("foo"), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkName("foo"), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name to same", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeName("")}, + fieldMask: []string{"name"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewNameNil(), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(2), // Version remains same even though row is updated + checkName(""), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update description", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeDescription("foo")}, + fieldMask: []string{"description"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentDescriptionNil(), + checkUpdateSetRequestNewDescription("foo"), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkDescription("foo"), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update description to same", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeDescription("")}, + fieldMask: []string{"description"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentDescriptionNil(), + checkUpdateSetRequestNewDescriptionNil(), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(2), // Version remains same even though row is updated + checkDescription(""), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "add preferred endpoints", + startingSet: setupBareHostSet, + changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})}, + fieldMask: []string{"PreferredEndpoints"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentPreferredEndpoints(nil), + checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(2), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update preferred endpoints", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})}, + fieldMask: []string{"PreferredEndpoints"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "clear preferred endpoints", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changePreferredEndpoints(nil)}, + fieldMask: []string{"PreferredEndpoints"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpointsNil(), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkPreferredEndpoints(nil), + checkNeedSync(false), + }, + }, + { + name: "update attributes (add)", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "baz": "qux", + })}, + fieldMask: []string{"attributes"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (overwrite)", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "foo": "baz", + })}, + fieldMask: []string{"attributes"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{ + "foo": "baz", + }), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (null)", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "foo": nil, + })}, + fieldMask: []string{"attributes"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{}), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (full null)", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeAttributesNil()}, + fieldMask: []string{"attributes"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{}), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update attributes (combined)", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{changeAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + })}, + fieldMask: []string{"attributes.a", "attributes.foo"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentAttributes(map[string]interface{}{ + "foo": "bar", + }), + checkUpdateSetRequestNewAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkAttributes(map[string]interface{}{ + "a": "b", + "foo": "baz", + }), + checkNeedSync(true), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, + { + name: "update name and preferred endpoints", + startingSet: setupHostSet, + changeFuncs: []changeHostSetFunc{ + changePreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + changeName("foo"), + }, + fieldMask: []string{"name", "PreferredEndpoints"}, + wantCheckPluginReqFuncs: []checkPluginReqFunc{ + checkUpdateSetRequestCurrentNameNil(), + checkUpdateSetRequestNewName("foo"), + checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}), + checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkUpdateSetRequestPersistedSecrets(map[string]interface{}{ + "one": "two", + }), + }, + wantCheckSetFuncs: []checkHostSetFunc{ + checkVersion(3), + checkName("foo"), + checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}), + checkNeedSync(false), + checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE), + }, + }, } for _, tt := range tests { @@ -1078,7 +1129,7 @@ func TestRepository_UpdateSet(t *testing.T) { }, } - origSet := setupHostSet(t, ctx) + origSet, wantedHosts := tt.startingSet(t, ctx) pluginMap := testPluginMap if tt.withEmptyPluginMap { @@ -1098,7 +1149,12 @@ func TestRepository_UpdateSet(t *testing.T) { scopeId = *tt.withScopeId } - gotUpdatedSet, gotHosts, gotPlugin, gotNumUpdated, err := repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask) + version := origSet.Version + if tt.version != 0 { + version = tt.version + } + + gotUpdatedSet, gotHosts, gotPlugin, gotNumUpdated, err := repo.UpdateSet(ctx, scopeId, workingSet, version, tt.fieldMask) t.Cleanup(func() { gotOnUpdateCallCount = 0 }) if tt.wantIsErr != 0 { require.Equal(db.NoRowsAffected, gotNumUpdated) @@ -1118,8 +1174,8 @@ func TestRepository_UpdateSet(t *testing.T) { assert.Equal(testCatalog.PluginId, gotPlugin.PublicId) // Also assert that the hosts returned by the request are the ones that belong to the set - wantHostMap := make(map[string]string, len(testHosts)) - for _, h := range testHosts { + wantHostMap := make(map[string]string, len(wantedHosts)) + for _, h := range wantedHosts { wantHostMap[h.PublicId] = h.ExternalId } gotHostMap := make(map[string]string, len(gotHosts)) From b16ce175e5c3fcdbe7db44fbf106b98dd49531cb Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 12 Nov 2021 09:46:40 -0700 Subject: [PATCH 70/71] Fix secrets clearing bug in update. Changes to update count behavior for catalogs (#1691) * Fix secret clearing bug in update. Always return 1 catalog updated if we can find a catalog w/ a matching version. --- internal/host/plugin/loopback.go | 20 ++++++++++++- internal/host/plugin/loopback_test.go | 28 +++++++++++++++++-- .../host/plugin/repository_host_catalog.go | 13 ++++----- .../plugin/repository_host_catalog_test.go | 7 +++-- .../host_catalog_service_test.go | 25 +++++++++++++++-- .../api/hostcatalogs/host_catalog_test.go | 10 +++++++ 6 files changed, 88 insertions(+), 15 deletions(-) diff --git a/internal/host/plugin/loopback.go b/internal/host/plugin/loopback.go index cfd3c06fd..34f32a69a 100644 --- a/internal/host/plugin/loopback.go +++ b/internal/host/plugin/loopback.go @@ -38,6 +38,7 @@ func NewLoopbackPlugin() plgpb.HostPluginServiceServer { } ret.OnCreateCatalogFn = ret.onCreateCatalog ret.OnCreateSetFn = ret.onCreateSet + ret.OnUpdateCatalogFn = ret.onUpdateCatalog ret.OnDeleteSetFn = ret.onDeleteSet ret.ListHostsFn = ret.listHosts return ret @@ -57,7 +58,7 @@ func (l *loopbackPlugin) onCreateCatalog(ctx context.Context, req *plgpb.OnCreat }, nil } } - return nil, nil + return &plgpb.OnCreateCatalogResponse{}, nil } func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) { @@ -95,6 +96,23 @@ func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSet return nil, nil } +func (l *loopbackPlugin) onUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) { + const op = "plugin.(loopbackPlugin).onUpdateCatalog" + if req == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil") + } + if cat := req.GetNewCatalog(); cat != nil { + if secrets := cat.GetSecrets(); secrets != nil { + return &plgpb.OnUpdateCatalogResponse{ + Persisted: &plgpb.HostCatalogPersisted{ + Secrets: secrets, + }, + }, nil + } + } + return &plgpb.OnUpdateCatalogResponse{}, nil +} + func (l *loopbackPlugin) onDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) { const op = "plugin.(loopbackPlugin).onDeleteSet" if req == nil { diff --git a/internal/host/plugin/loopback_test.go b/internal/host/plugin/loopback_test.go index 6bd0dfcf1..691a79736 100644 --- a/internal/host/plugin/loopback_test.go +++ b/internal/host/plugin/loopback_test.go @@ -19,8 +19,8 @@ func TestLoopbackPlugin(t *testing.T) { plg := NewLoopbackPlugin() secretsMap := map[string]interface{}{ - "foo": "bar", - "baz": true, + "key1": "key2", + "baz": true, } secrets, err := structpb.NewStruct(secretsMap) require.NoError(err) @@ -38,6 +38,30 @@ func TestLoopbackPlugin(t *testing.T) { require.NotNil(catResp.GetPersisted().GetSecrets()) assert.EqualValues(secretsMap, catResp.GetPersisted().GetSecrets().AsMap()) + newSecretsMap := map[string]interface{}{ + "key1": "key2", + "baz": true, + } + newSecrets, err := structpb.NewStruct(newSecretsMap) + require.NoError(err) + + // First, test that if we give it secrets, those secrets come back as + // persisted data + upResp, err := plg.OnUpdateCatalog(ctx, &plgpb.OnUpdateCatalogRequest{ + CurrentCatalog: &hostcatalogs.HostCatalog{}, + NewCatalog: &hostcatalogs.HostCatalog{ + Secrets: newSecrets, + }, + Persisted: &plgpb.HostCatalogPersisted{ + Secrets: secrets, + }, + }) + require.NoError(err) + require.NotNil(upResp) + require.NotNil(upResp.GetPersisted()) + require.NotNil(upResp.GetPersisted().GetSecrets()) + assert.EqualValues(newSecretsMap, upResp.GetPersisted().GetSecrets().AsMap()) + // Add data to some sets hostInfo1 := map[string]interface{}{ loopbackPluginHostInfoAttrField: map[string]interface{}{ diff --git a/internal/host/plugin/repository_host_catalog.go b/internal/host/plugin/repository_host_catalog.go index 42afeb81c..74464774a 100644 --- a/internal/host/plugin/repository_host_catalog.go +++ b/internal/host/plugin/repository_host_catalog.go @@ -247,11 +247,8 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version case strings.EqualFold("secrets", strings.Split(f, ".")[0]): // While in a similar format, secrets are passed along // wholesale (for the time being). Don't append this mask - // field, as secrets do not have a database entry. Clear the - // secrets out of the working set after. + // field, as secrets do not have a database entry. newCatalog.Secrets = c.Secrets - c.Secrets = nil - default: return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } @@ -443,10 +440,10 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", newCatalog.PublicId))) } - var numUpdated int - if recordUpdated { - numUpdated = 1 - } + // Even if we didn't update any records, if we were able to find the record + // with the appropriate version, returning 1 row updated is consistant with + // static's update catalog behavior. + numUpdated := 1 return returnedCatalog, plg, numUpdated, nil } diff --git a/internal/host/plugin/repository_host_catalog_test.go b/internal/host/plugin/repository_host_catalog_test.go index 9eb7fb665..832a07964 100644 --- a/internal/host/plugin/repository_host_catalog_test.go +++ b/internal/host/plugin/repository_host_catalog_test.go @@ -987,9 +987,10 @@ func TestRepository_UpdateCatalog(t *testing.T) { name: "update secrets", changeFuncs: []changeHostCatalogFunc{changeSecrets(map[string]interface{}{ "three": "four", + "five": "six", })}, version: 2, - fieldMask: []string{"secrets.three"}, + fieldMask: []string{"secrets.three", "secrets.five"}, wantCheckFuncs: []checkFunc{ checkVersion(3), checkUpdateCatalogRequestPersistedSecrets(map[string]interface{}{ @@ -997,9 +998,11 @@ func TestRepository_UpdateCatalog(t *testing.T) { }), checkUpdateCatalogRequestSecrets(map[string]interface{}{ "three": "four", + "five": "six", }), checkSecrets(map[string]interface{}{ "three": "four", + "five": "six", }), checkNumUpdated(1), checkVerifyCatalogOplog(oplog.OpType_OP_TYPE_UPDATE), @@ -1024,7 +1027,7 @@ func TestRepository_UpdateCatalog(t *testing.T) { checkSecrets(map[string]interface{}{ "one": "two", }), - checkNumUpdated(db.NoRowsAffected), + checkNumUpdated(1), }, }, { diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 515de1058..95863b6be 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -1354,7 +1354,7 @@ func TestUpdate_Plugin(t *testing.T) { name := "test" plg := host.TestPlugin(t, conn, name) plgm := map[string]plgpb.HostPluginServiceClient{ - plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}), + plg.GetPublicId(): plugin.NewWrappingPluginClient(plugin.NewLoopbackPlugin()), } repoFn := func() (*static.Repository, error) { @@ -1422,6 +1422,11 @@ func TestUpdate_Plugin(t *testing.T) { c.Attributes = i } } + updateSecrets := func(i *structpb.Struct) updateFn { + return func(c *pb.HostCatalog) { + c.Secrets = i + } + } cases := []struct { name string @@ -1461,6 +1466,20 @@ func TestUpdate_Plugin(t *testing.T) { assert.Equal(t, map[string]interface{}{"newkey": "newvalue"}, in.GetAttributes().AsMap()) }, }, + { + name: "Update secrets", + masks: []string{"secrets.key1", "secrets.key2"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateSecrets(func() *structpb.Struct { + attr, err := structpb.NewStruct(map[string]interface{}{ + "key1": "val1", + }) + require.NoError(t, err) + return attr + }()), + }, + }, { name: "Multiple Paths in single string", masks: []string{"name,description"}, @@ -1615,7 +1634,9 @@ func TestUpdate_Plugin(t *testing.T) { item := got.GetItem() assert.Equal(uint32(2), item.GetVersion()) assert.Greater(item.GetUpdatedTime().AsTime().UnixNano(), item.GetCreatedTime().AsTime().UnixNano()) - tc.check(t, item) + if tc.check != nil { + tc.check(t, item) + } }) } } diff --git a/internal/tests/api/hostcatalogs/host_catalog_test.go b/internal/tests/api/hostcatalogs/host_catalog_test.go index d7c74c39d..b23d1a4b8 100644 --- a/internal/tests/api/hostcatalogs/host_catalog_test.go +++ b/internal/tests/api/hostcatalogs/host_catalog_test.go @@ -149,6 +149,9 @@ func TestCrud(t *testing.T) { hc, err = hcClient.Read(tc.Context(), hc.Item.Id) checkCatalog("read", hc.Item, err, "foo", 1) + hc, err = hcClient.Update(tc.Context(), hc.Item.Id, hc.Item.Version, hostcatalogs.WithName("foo")) + checkCatalog("update", hc.Item, err, "foo", 1) + hc, err = hcClient.Update(tc.Context(), hc.Item.Id, hc.Item.Version, hostcatalogs.WithName("bar")) checkCatalog("update", hc.Item, err, "bar", 2) @@ -180,6 +183,13 @@ func TestCrud(t *testing.T) { c, err = hcClient.Update(tc.Context(), c.Item.Id, c.Item.Version, hostcatalogs.DefaultName()) checkCatalog("update", c.Item, err, "", 3) + c, err = hcClient.Update(tc.Context(), c.Item.Id, 0, hostcatalogs.WithAutomaticVersioning(true), + hostcatalogs.WithSecrets(map[string]interface{}{ + "key1": "val1", + "key2": "val2", + })) + checkCatalog("update", c.Item, err, "", 4) + c, err = hcClient.Read(tc.Context(), c.Item.Id) assert.NoError(err) From 5850001a073f5dfb167435eeec1050375c7968bc Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Fri, 12 Nov 2021 09:53:56 -0700 Subject: [PATCH 71/71] Rename file to match others in directory and remove TODO. --- .../postgres/20/{03_hostplugin.up.sql => 03_host_plugin.up.sql} | 2 -- 1 file changed, 2 deletions(-) rename internal/db/schema/migrations/oss/postgres/20/{03_hostplugin.up.sql => 03_host_plugin.up.sql} (94%) diff --git a/internal/db/schema/migrations/oss/postgres/20/03_hostplugin.up.sql b/internal/db/schema/migrations/oss/postgres/20/03_host_plugin.up.sql similarity index 94% rename from internal/db/schema/migrations/oss/postgres/20/03_hostplugin.up.sql rename to internal/db/schema/migrations/oss/postgres/20/03_host_plugin.up.sql index ad17f82ca..60c35ea24 100644 --- a/internal/db/schema/migrations/oss/postgres/20/03_hostplugin.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/03_host_plugin.up.sql @@ -26,8 +26,6 @@ begin; create table plugin_host ( public_id wt_plugin_id primary key, scope_id wt_scope_id not null - -- TODO: Allow plugins to be created in different scopes and - -- constrain the host-catalog's plugin reference accordingly. constraint iam_scope_global_fkey references iam_scope_global(scope_id) on delete cascade