Add Link resource type and controller skeleton (#19788)

* Add HCCLink resource type

* Register HCCLink resource type with basic validation

* Add validation for required fields

* Add test for default ACLs

* Add no-op controller for HCCLink

* Add resource-apis semantic validation check in hcclink controller

* Add copyright headers

* Rename HCCLink to Link

* Add hcp_cluster_url to link proto

* Update 'disabled' reason with more detail

* Update link status name to consul.io/hcp/link

* Change link version from v1 to v2

* Use feature flag/experiment to enable v2 resources with HCP
This commit is contained in:
Nick Cellino 2024-01-09 13:57:59 -05:00 committed by GitHub
parent dbcba7aec7
commit 0deebaf637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 867 additions and 11 deletions

View File

@ -1140,8 +1140,7 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
// TODO(CC-6389): Remove once resource-apis is no longer considered experimental and is supported by HCP // TODO(CC-6389): Remove once resource-apis is no longer considered experimental and is supported by HCP
if stringslice.Contains(rt.Experiments, consul.CatalogResourceExperimentName) && rt.IsCloudEnabled() { if stringslice.Contains(rt.Experiments, consul.CatalogResourceExperimentName) && rt.IsCloudEnabled() {
// Allow override of this check for development/testing purposes. Should not be used in production // Allow override of this check for development/testing purposes. Should not be used in production
override, err := strconv.ParseBool(os.Getenv("CONSUL_OVERRIDE_HCP_RESOURCE_APIS_CHECK")) if !stringslice.Contains(rt.Experiments, consul.HCPAllowV2ResourceAPIs) {
if err != nil || !override {
return RuntimeConfig{}, fmt.Errorf("`experiments` cannot include 'resource-apis' when HCP `cloud` configuration is set") return RuntimeConfig{}, fmt.Errorf("`experiments` cannot include 'resource-apis' when HCP `cloud` configuration is set")
} }
} }

View File

@ -581,7 +581,6 @@ func TestBuilder_WarnCloudConfigWithResourceApis(t *testing.T) {
name string name string
hcl string hcl string
expectErr bool expectErr bool
override bool
}{ }{
{ {
name: "base_case", name: "base_case",
@ -611,9 +610,8 @@ func TestBuilder_WarnCloudConfigWithResourceApis(t *testing.T) {
{ {
name: "cloud-config_resource-apis_experiment_override", name: "cloud-config_resource-apis_experiment_override",
hcl: ` hcl: `
experiments = ["resource-apis"] experiments = ["resource-apis", "hcp-v2-resource-apis"]
cloud{ resource_id = "abc" client_id = "abc" client_secret = "abc"}`, cloud{ resource_id = "abc" client_id = "abc" client_secret = "abc"}`,
override: true,
}, },
} }
for _, tc := range tests { for _, tc := range tests {
@ -629,13 +627,7 @@ func TestBuilder_WarnCloudConfigWithResourceApis(t *testing.T) {
}, },
}, },
} }
if tc.override {
os.Setenv("CONSUL_OVERRIDE_HCP_RESOURCE_APIS_CHECK", "1")
}
_, err := Load(builderOpts) _, err := Load(builderOpts)
if tc.override {
os.Unsetenv("CONSUL_OVERRIDE_HCP_RESOURCE_APIS_CHECK")
}
if tc.expectErr { if tc.expectErr {
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "cannot include 'resource-apis' when HCP") require.Contains(t, err.Error(), "cannot include 'resource-apis' when HCP")

View File

@ -67,6 +67,15 @@ func (d Deps) UseV2Tenancy() bool {
return false return false
} }
// HCPAllowV2Resources returns true if "hcp-v2-resource-apis" is present in the Experiments
// array of the agent config.
func (d Deps) HCPAllowV2Resources() bool {
if stringslice.Contains(d.Experiments, HCPAllowV2ResourceAPIs) {
return true
}
return false
}
type GRPCClientConner interface { type GRPCClientConner interface {
ClientConn(datacenter string) (*grpc.ClientConn, error) ClientConn(datacenter string) (*grpc.ClientConn, error)
ClientConnLeader() (*grpc.ClientConn, error) ClientConnLeader() (*grpc.ClientConn, error)

View File

@ -75,6 +75,7 @@ import (
"github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/auth"
"github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/controller"
hcpctl "github.com/hashicorp/consul/internal/hcp"
"github.com/hashicorp/consul/internal/mesh" "github.com/hashicorp/consul/internal/mesh"
proxysnapshot "github.com/hashicorp/consul/internal/mesh/proxy-snapshot" proxysnapshot "github.com/hashicorp/consul/internal/mesh/proxy-snapshot"
"github.com/hashicorp/consul/internal/multicluster" "github.com/hashicorp/consul/internal/multicluster"
@ -142,6 +143,7 @@ const (
CatalogResourceExperimentName = "resource-apis" CatalogResourceExperimentName = "resource-apis"
V2TenancyExperimentName = "v2tenancy" V2TenancyExperimentName = "v2tenancy"
HCPAllowV2ResourceAPIs = "hcp-v2-resource-apis"
) )
const ( const (
@ -477,6 +479,10 @@ type Server struct {
// useV2Tenancy is tied to the "v2tenancy" feature flag. // useV2Tenancy is tied to the "v2tenancy" feature flag.
useV2Tenancy bool useV2Tenancy bool
// whether v2 resources are enabled for use with HCP
// TODO(CC-6389): Remove once resource-apis is no longer considered experimental and is supported by HCP
hcpAllowV2Resources bool
} }
func (s *Server) DecrementBlockingQueries() uint64 { func (s *Server) DecrementBlockingQueries() uint64 {
@ -567,6 +573,7 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server,
registry: flat.Registry, registry: flat.Registry,
useV2Resources: flat.UseV2Resources(), useV2Resources: flat.UseV2Resources(),
useV2Tenancy: flat.UseV2Tenancy(), useV2Tenancy: flat.UseV2Tenancy(),
hcpAllowV2Resources: flat.HCPAllowV2Resources(),
} }
incomingRPCLimiter.Register(s) incomingRPCLimiter.Register(s)
@ -961,6 +968,11 @@ func isV1CatalogRequest(rpcName string) bool {
} }
func (s *Server) registerControllers(deps Deps, proxyUpdater ProxyUpdater) error { func (s *Server) registerControllers(deps Deps, proxyUpdater ProxyUpdater) error {
hcpctl.RegisterControllers(s.controllerManager, hcpctl.ControllerDependencies{
ResourceApisEnabled: s.useV2Resources,
HCPAllowV2ResourceApis: s.hcpAllowV2Resources,
})
// When not enabled, the v1 tenancy bridge is used by default. // When not enabled, the v1 tenancy bridge is used by default.
if s.useV2Tenancy { if s.useV2Tenancy {
tenancy.RegisterControllers( tenancy.RegisterControllers(

View File

@ -19,6 +19,7 @@ flowchart TD
demo/v1/recordlabel demo/v1/recordlabel
demo/v2/album demo/v2/album
demo/v2/artist demo/v2/artist
hcp/v2/link
internal/v1/tombstone internal/v1/tombstone
mesh/v2beta1/computedexplicitdestinations --> catalog/v2beta1/service mesh/v2beta1/computedexplicitdestinations --> catalog/v2beta1/service
mesh/v2beta1/computedexplicitdestinations --> catalog/v2beta1/workload mesh/v2beta1/computedexplicitdestinations --> catalog/v2beta1/workload

View File

@ -6,6 +6,7 @@ package consul
import ( import (
"github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/auth"
"github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/hcp"
"github.com/hashicorp/consul/internal/mesh" "github.com/hashicorp/consul/internal/mesh"
"github.com/hashicorp/consul/internal/multicluster" "github.com/hashicorp/consul/internal/multicluster"
"github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource"
@ -29,6 +30,7 @@ func NewTypeRegistry() resource.Registry {
auth.RegisterTypes(registry) auth.RegisterTypes(registry)
tenancy.RegisterTypes(registry) tenancy.RegisterTypes(registry)
multicluster.RegisterTypes(registry) multicluster.RegisterTypes(registry)
hcp.RegisterTypes(registry)
return registry return registry
} }

25
internal/hcp/exports.go Normal file
View File

@ -0,0 +1,25 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package hcp
import (
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/hcp/internal/controllers"
"github.com/hashicorp/consul/internal/hcp/internal/types"
"github.com/hashicorp/consul/internal/resource"
)
// RegisterTypes adds all resource types within the "hcp" API group
// to the given type registry
func RegisterTypes(r resource.Registry) {
types.Register(r)
}
type ControllerDependencies = controllers.Dependencies
// RegisterControllers registers controllers for the catalog types with
// the given controller Manager.
func RegisterControllers(mgr *controller.Manager, deps ControllerDependencies) {
controllers.Register(mgr, deps)
}

View File

@ -0,0 +1,82 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package link
import (
"context"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/proto-public/pbresource"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/hashicorp/consul/internal/controller"
pbhcp "github.com/hashicorp/consul/proto-public/pbhcp/v2"
)
func LinkController(resourceApisEnabled bool, hcpAllowV2ResourceApis bool) *controller.Controller {
return controller.NewController("link", pbhcp.LinkType).
WithReconciler(&linkReconciler{
resourceApisEnabled: resourceApisEnabled,
hcpAllowV2ResourceApis: hcpAllowV2ResourceApis,
})
}
type linkReconciler struct {
resourceApisEnabled bool
hcpAllowV2ResourceApis bool
}
func (r *linkReconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error {
// The runtime is passed by value so replacing it here for the remainder of this
// reconciliation request processing will not affect future invocations.
rt.Logger = rt.Logger.With("resource-id", req.ID, "controller", StatusKey)
rt.Logger.Trace("reconciling link")
rsp, err := rt.Client.Read(ctx, &pbresource.ReadRequest{Id: req.ID})
switch {
case status.Code(err) == codes.NotFound:
rt.Logger.Trace("link has been deleted")
return nil
case err != nil:
rt.Logger.Error("the resource service has returned an unexpected error", "error", err)
return err
}
res := rsp.Resource
var link pbhcp.Link
if err := res.Data.UnmarshalTo(&link); err != nil {
rt.Logger.Error("error unmarshalling link data", "error", err)
return err
}
var newStatus *pbresource.Status
if r.resourceApisEnabled && !r.hcpAllowV2ResourceApis {
newStatus = &pbresource.Status{
ObservedGeneration: res.Generation,
Conditions: []*pbresource.Condition{ConditionDisabled},
}
} else {
newStatus = &pbresource.Status{
ObservedGeneration: res.Generation,
Conditions: []*pbresource.Condition{ConditionLinked(link.ResourceId)},
}
}
if resource.EqualStatus(res.Status[StatusKey], newStatus, false) {
return nil
}
_, err = rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{
Id: res.Id,
Key: StatusKey,
Status: newStatus,
})
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,122 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package link
import (
"context"
"testing"
"github.com/stretchr/testify/suite"
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/hcp/internal/types"
"github.com/hashicorp/consul/internal/resource/resourcetest"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
pbhcp "github.com/hashicorp/consul/proto-public/pbhcp/v2"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/consul/sdk/testutil"
)
type controllerSuite struct {
suite.Suite
ctx context.Context
client *rtest.Client
rt controller.Runtime
ctl linkReconciler
tenancies []*pbresource.Tenancy
}
func (suite *controllerSuite) SetupTest() {
suite.ctx = testutil.TestContext(suite.T())
suite.tenancies = resourcetest.TestTenancies()
client := svctest.NewResourceServiceBuilder().
WithRegisterFns(types.Register).
WithTenancies(suite.tenancies...).
Run(suite.T())
suite.rt = controller.Runtime{
Client: client,
Logger: testutil.Logger(suite.T()),
}
suite.client = rtest.NewClient(client)
}
func TestLinkController(t *testing.T) {
suite.Run(t, new(controllerSuite))
}
func (suite *controllerSuite) deleteResourceFunc(id *pbresource.ID) func() {
return func() {
suite.client.MustDelete(suite.T(), id)
}
}
func (suite *controllerSuite) TestController_Ok() {
// Run the controller manager
mgr := controller.NewManager(suite.client, suite.rt.Logger)
mgr.Register(LinkController(false, false))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
linkData := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
link := rtest.Resource(pbhcp.LinkType, "global").
WithData(suite.T(), linkData).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(link.Id))
suite.client.WaitForStatusCondition(suite.T(), link.Id, StatusKey, ConditionLinked(linkData.ResourceId))
}
func (suite *controllerSuite) TestControllerResourceApisEnabled_LinkDisabled() {
// Run the controller manager
mgr := controller.NewManager(suite.client, suite.rt.Logger)
mgr.Register(LinkController(true, false))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
linkData := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
// The controller is currently a no-op, so there is nothing to test other than making sure we do not panic
link := rtest.Resource(pbhcp.LinkType, "global").
WithData(suite.T(), linkData).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(link.Id))
suite.client.WaitForStatusCondition(suite.T(), link.Id, StatusKey, ConditionDisabled)
}
func (suite *controllerSuite) TestControllerResourceApisEnabledWithOverride_LinkNotDisabled() {
// Run the controller manager
mgr := controller.NewManager(suite.client, suite.rt.Logger)
mgr.Register(LinkController(true, true))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
linkData := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
// The controller is currently a no-op, so there is nothing to test other than making sure we do not panic
link := rtest.Resource(pbhcp.LinkType, "global").
WithData(suite.T(), linkData).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(link.Id))
suite.client.WaitForStatusCondition(suite.T(), link.Id, StatusKey, ConditionLinked(linkData.ResourceId))
}

View File

@ -0,0 +1,39 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package link
import (
"fmt"
"github.com/hashicorp/consul/proto-public/pbresource"
)
const (
StatusKey = "consul.io/hcp/link"
StatusLinked = "linked"
LinkedReason = "SUCCESS"
DisabledReasonV2ResourcesUnsupported = "DISABLED_V2_RESOURCES_UNSUPPORTED"
LinkedMessageFormat = "Successfully linked to cluster '%s'"
DisabledResourceAPIsEnabledMessage = "Link is disabled because resource-apis are enabled"
)
var (
ConditionDisabled = &pbresource.Condition{
Type: StatusLinked,
State: pbresource.Condition_STATE_FALSE,
Reason: DisabledReasonV2ResourcesUnsupported,
Message: DisabledResourceAPIsEnabledMessage,
}
)
func ConditionLinked(resourceId string) *pbresource.Condition {
return &pbresource.Condition{
Type: StatusLinked,
State: pbresource.Condition_STATE_TRUE,
Reason: LinkedReason,
Message: fmt.Sprintf(LinkedMessageFormat, resourceId),
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package controllers
import (
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/hcp/internal/controllers/link"
)
type Dependencies struct {
ResourceApisEnabled bool
HCPAllowV2ResourceApis bool
}
func Register(mgr *controller.Manager, deps Dependencies) {
mgr.Register(link.LinkController(
deps.ResourceApisEnabled,
deps.HCPAllowV2ResourceApis,
))
}

View File

@ -0,0 +1,63 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package types
import (
"errors"
"github.com/hashicorp/consul/internal/resource"
pbhcp "github.com/hashicorp/consul/proto-public/pbhcp/v2"
"github.com/hashicorp/go-multierror"
)
type DecodedLink = resource.DecodedResource[*pbhcp.Link]
var (
linkConfigurationNameError = errors.New("only a single Link resource is allowed and it must be named global")
)
func RegisterLink(r resource.Registry) {
r.Register(resource.Registration{
Type: pbhcp.LinkType,
Proto: &pbhcp.Link{},
Scope: resource.ScopeCluster,
Validate: ValidateLink,
})
}
var ValidateLink = resource.DecodeAndValidate(validateLink)
func validateLink(res *DecodedLink) error {
var err error
if res.Id.Name != "global" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "name",
Wrapped: linkConfigurationNameError,
})
}
if res.Data.ClientId == "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "client_id",
Wrapped: resource.ErrMissing,
})
}
if res.Data.ClientSecret == "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "client_secret",
Wrapped: resource.ErrMissing,
})
}
if res.Data.ResourceId == "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "resource_id",
Wrapped: resource.ErrMissing,
})
}
return err
}

View File

@ -0,0 +1,185 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package types
import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
"github.com/hashicorp/consul/internal/resource"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
pbhcp "github.com/hashicorp/consul/proto-public/pbhcp/v2"
"github.com/hashicorp/consul/proto-public/pbresource"
)
func createCloudLinkResource(t *testing.T, data protoreflect.ProtoMessage) *pbresource.Resource {
res := &pbresource.Resource{
Id: &pbresource.ID{
Type: pbhcp.LinkType,
Name: "global",
},
}
var err error
res.Data, err = anypb.New(data)
require.NoError(t, err)
return res
}
func TestValidateLink_Ok(t *testing.T) {
data := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
res := createCloudLinkResource(t, data)
err := ValidateLink(res)
require.NoError(t, err)
}
func TestValidateLink_ParseError(t *testing.T) {
// Any type other than the Link type would work
// to cause the error we are expecting
data := &pbcatalog.IP{Address: "198.18.0.1"}
res := createCloudLinkResource(t, data)
err := ValidateLink(res)
require.Error(t, err)
require.ErrorAs(t, err, &resource.ErrDataParse{})
}
func TestValidateLink_InvalidName(t *testing.T) {
data := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
res := createCloudLinkResource(t, data)
res.Id.Name = "default"
err := ValidateLink(res)
expected := resource.ErrInvalidField{
Name: "name",
Wrapped: linkConfigurationNameError,
}
var actual resource.ErrInvalidField
require.ErrorAs(t, err, &actual)
require.Equal(t, expected, actual)
}
func TestValidateLink_MissingClientId(t *testing.T) {
data := &pbhcp.Link{
ClientId: "",
ClientSecret: "abc",
ResourceId: "abc",
}
res := createCloudLinkResource(t, data)
err := ValidateLink(res)
expected := resource.ErrInvalidField{
Name: "client_id",
Wrapped: resource.ErrMissing,
}
var actual resource.ErrInvalidField
require.ErrorAs(t, err, &actual)
require.Equal(t, expected, actual)
}
func TestValidateLink_MissingClientSecret(t *testing.T) {
data := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "",
ResourceId: "abc",
}
res := createCloudLinkResource(t, data)
err := ValidateLink(res)
expected := resource.ErrInvalidField{
Name: "client_secret",
Wrapped: resource.ErrMissing,
}
var actual resource.ErrInvalidField
require.ErrorAs(t, err, &actual)
require.Equal(t, expected, actual)
}
func TestValidateLink_MissingResourceId(t *testing.T) {
data := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "",
}
res := createCloudLinkResource(t, data)
err := ValidateLink(res)
expected := resource.ErrInvalidField{
Name: "resource_id",
Wrapped: resource.ErrMissing,
}
var actual resource.ErrInvalidField
require.ErrorAs(t, err, &actual)
require.Equal(t, expected, actual)
}
// Currently, we have no specific ACLs configured so the default `operator` permissions are required
func TestLinkACLs(t *testing.T) {
registry := resource.NewRegistry()
RegisterLink(registry)
data := &pbhcp.Link{
ClientId: "abc",
ClientSecret: "abc",
ResourceId: "abc",
}
link := createCloudLinkResource(t, data)
cases := map[string]rtest.ACLTestCase{
"no rules": {
Rules: ``,
Res: link,
ReadOK: rtest.DENY,
WriteOK: rtest.DENY,
ListOK: rtest.DENY,
},
"link test read": {
Rules: `operator = "read"`,
Res: link,
ReadOK: rtest.ALLOW,
WriteOK: rtest.DENY,
ListOK: rtest.ALLOW,
},
"link test write": {
Rules: `operator = "write"`,
Res: link,
ReadOK: rtest.ALLOW,
WriteOK: rtest.ALLOW,
ListOK: rtest.ALLOW,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rtest.RunACLTestCase(t, tc, registry)
})
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package types
import "github.com/hashicorp/consul/internal/resource"
func Register(r resource.Registry) {
RegisterLink(r)
}

View File

@ -0,0 +1,18 @@
// Code generated by protoc-gen-go-binary. DO NOT EDIT.
// source: pbhcp/v2/link.proto
package hcpv2
import (
"google.golang.org/protobuf/proto"
)
// MarshalBinary implements encoding.BinaryMarshaler
func (msg *Link) MarshalBinary() ([]byte, error) {
return proto.Marshal(msg)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (msg *Link) UnmarshalBinary(b []byte) error {
return proto.Unmarshal(b, msg)
}

View File

@ -0,0 +1,191 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc (unknown)
// source: pbhcp/v2/link.proto
package hcpv2
import (
_ "github.com/hashicorp/consul/proto-public/pbresource"
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 Link struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
ClientSecret string `protobuf:"bytes,3,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
HcpClusterUrl string `protobuf:"bytes,4,opt,name=hcp_cluster_url,json=hcpClusterUrl,proto3" json:"hcp_cluster_url,omitempty"`
}
func (x *Link) Reset() {
*x = Link{}
if protoimpl.UnsafeEnabled {
mi := &file_pbhcp_v2_link_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Link) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Link) ProtoMessage() {}
func (x *Link) ProtoReflect() protoreflect.Message {
mi := &file_pbhcp_v2_link_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 Link.ProtoReflect.Descriptor instead.
func (*Link) Descriptor() ([]byte, []int) {
return file_pbhcp_v2_link_proto_rawDescGZIP(), []int{0}
}
func (x *Link) GetResourceId() string {
if x != nil {
return x.ResourceId
}
return ""
}
func (x *Link) GetClientId() string {
if x != nil {
return x.ClientId
}
return ""
}
func (x *Link) GetClientSecret() string {
if x != nil {
return x.ClientSecret
}
return ""
}
func (x *Link) GetHcpClusterUrl() string {
if x != nil {
return x.HcpClusterUrl
}
return ""
}
var File_pbhcp_v2_link_proto protoreflect.FileDescriptor
var file_pbhcp_v2_link_proto_rawDesc = []byte{
0x0a, 0x13, 0x70, 0x62, 0x68, 0x63, 0x70, 0x2f, 0x76, 0x32, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x68, 0x63, 0x70, 0x2e, 0x76, 0x32, 0x1a, 0x1c,
0x70, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x01, 0x0a,
0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65,
0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x63, 0x70, 0x5f,
0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0d, 0x68, 0x63, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x6c,
0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x01, 0x42, 0xe0, 0x01, 0x0a, 0x1b, 0x63, 0x6f, 0x6d,
0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75,
0x6c, 0x2e, 0x68, 0x63, 0x70, 0x2e, 0x76, 0x32, 0x42, 0x09, 0x4c, 0x69, 0x6e, 0x6b, 0x50, 0x72,
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73,
0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f,
0x70, 0x62, 0x68, 0x63, 0x70, 0x2f, 0x76, 0x32, 0x3b, 0x68, 0x63, 0x70, 0x76, 0x32, 0xa2, 0x02,
0x03, 0x48, 0x43, 0x48, 0xaa, 0x02, 0x17, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x48, 0x63, 0x70, 0x2e, 0x56, 0x32, 0xca, 0x02,
0x17, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75,
0x6c, 0x5c, 0x48, 0x63, 0x70, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x23, 0x48, 0x61, 0x73, 0x68, 0x69,
0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x48, 0x63, 0x70, 0x5c,
0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x1a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73,
0x75, 0x6c, 0x3a, 0x3a, 0x48, 0x63, 0x70, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_pbhcp_v2_link_proto_rawDescOnce sync.Once
file_pbhcp_v2_link_proto_rawDescData = file_pbhcp_v2_link_proto_rawDesc
)
func file_pbhcp_v2_link_proto_rawDescGZIP() []byte {
file_pbhcp_v2_link_proto_rawDescOnce.Do(func() {
file_pbhcp_v2_link_proto_rawDescData = protoimpl.X.CompressGZIP(file_pbhcp_v2_link_proto_rawDescData)
})
return file_pbhcp_v2_link_proto_rawDescData
}
var file_pbhcp_v2_link_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_pbhcp_v2_link_proto_goTypes = []interface{}{
(*Link)(nil), // 0: hashicorp.consul.hcp.v2.Link
}
var file_pbhcp_v2_link_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_pbhcp_v2_link_proto_init() }
func file_pbhcp_v2_link_proto_init() {
if File_pbhcp_v2_link_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pbhcp_v2_link_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Link); 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_pbhcp_v2_link_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pbhcp_v2_link_proto_goTypes,
DependencyIndexes: file_pbhcp_v2_link_proto_depIdxs,
MessageInfos: file_pbhcp_v2_link_proto_msgTypes,
}.Build()
File_pbhcp_v2_link_proto = out.File
file_pbhcp_v2_link_proto_rawDesc = nil
file_pbhcp_v2_link_proto_goTypes = nil
file_pbhcp_v2_link_proto_depIdxs = nil
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
package hashicorp.consul.hcp.v2;
import "pbresource/annotations.proto";
message Link {
option (hashicorp.consul.resource.spec) = {scope: SCOPE_CLUSTER};
string resource_id = 1;
string client_id = 2;
string client_secret = 3;
string hcp_cluster_url = 4;
}

View File

@ -0,0 +1,27 @@
// Code generated by protoc-gen-deepcopy. DO NOT EDIT.
package hcpv2
import (
proto "google.golang.org/protobuf/proto"
)
// DeepCopyInto supports using Link within kubernetes types, where deepcopy-gen is used.
func (in *Link) DeepCopyInto(out *Link) {
proto.Reset(out)
proto.Merge(out, proto.Clone(in))
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Link. Required by controller-gen.
func (in *Link) DeepCopy() *Link {
if in == nil {
return nil
}
out := new(Link)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Link. Required by controller-gen.
func (in *Link) DeepCopyInterface() interface{} {
return in.DeepCopy()
}

View File

@ -0,0 +1,22 @@
// Code generated by protoc-json-shim. DO NOT EDIT.
package hcpv2
import (
protojson "google.golang.org/protobuf/encoding/protojson"
)
// MarshalJSON is a custom marshaler for Link
func (this *Link) MarshalJSON() ([]byte, error) {
str, err := LinkMarshaler.Marshal(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for Link
func (this *Link) UnmarshalJSON(b []byte) error {
return LinkUnmarshaler.Unmarshal(b, this)
}
var (
LinkMarshaler = &protojson.MarshalOptions{}
LinkUnmarshaler = &protojson.UnmarshalOptions{DiscardUnknown: false}
)

View File

@ -0,0 +1,22 @@
// Code generated by protoc-gen-resource-types. DO NOT EDIT.
package hcpv2
import (
"github.com/hashicorp/consul/proto-public/pbresource"
)
const (
GroupName = "hcp"
Version = "v2"
LinkKind = "Link"
)
var (
LinkType = &pbresource.Type{
Group: GroupName,
GroupVersion: Version,
Kind: LinkKind,
}
)