diff --git a/agent/consul/leader.go b/agent/consul/leader.go index fcbf794541..86ac10bb12 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -5,6 +5,7 @@ package consul import ( "context" + "errors" "fmt" "net" "reflect" @@ -16,20 +17,27 @@ import ( "github.com/armon/go-metrics" "github.com/armon/go-metrics/prometheus" + "github.com/oklog/ulid/v2" + "golang.org/x/time/rate" + "google.golang.org/protobuf/types/known/anypb" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-uuid" "github.com/hashicorp/go-version" "github.com/hashicorp/raft" "github.com/hashicorp/serf/serf" - "golang.org/x/time/rate" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/metadata" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs/aclfilter" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/storage" "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/logging" + "github.com/hashicorp/consul/proto-public/pbresource" + pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1" "github.com/hashicorp/consul/types" ) @@ -341,6 +349,12 @@ func (s *Server) establishLeadership(ctx context.Context) error { s.startLogVerification(ctx) } + if s.useV2Tenancy { + if err := s.initTenancy(ctx, s.resourceServiceServer.Backend); err != nil { + return err + } + } + if s.config.Reporting.License.Enabled && s.reportingManager != nil { s.reportingManager.StartReportingAgent() } @@ -1449,3 +1463,61 @@ func (s *serversIntentionsAsConfigEntriesInfo) update(srv *metadata.Server) bool // prevent continuing server evaluation return false } + +func (s *Server) initTenancy(ctx context.Context, b storage.Backend) error { + // we write these defaults directly to the storage backend + // without going through the resource service since tenancy + // validation hooks block writes to the default namespace + // and partition. + if err := s.createDefaultPartition(ctx, b); err != nil { + return err + } + + if err := s.createDefaultNamespace(ctx, b); err != nil { + return err + } + return nil +} + +func (s *Server) createDefaultNamespace(ctx context.Context, b storage.Backend) error { + readID := &pbresource.ID{ + Type: pbtenancy.NamespaceType, + Name: resource.DefaultNamespaceName, + Tenancy: resource.DefaultPartitionedTenancy(), + } + + read, err := b.Read(ctx, storage.StrongConsistency, readID) + + if err != nil && !errors.Is(err, storage.ErrNotFound) { + return fmt.Errorf("failed to read the %q namespace: %v", resource.DefaultNamespaceName, err) + } + if read == nil && errors.Is(err, storage.ErrNotFound) { + nsData, err := anypb.New(&pbtenancy.Namespace{Description: "default namespace in default partition"}) + if err != nil { + return err + } + + // create a default namespace in default partition + nsID := &pbresource.ID{ + Type: pbtenancy.NamespaceType, + Name: resource.DefaultNamespaceName, + Tenancy: resource.DefaultPartitionedTenancy(), + Uid: ulid.Make().String(), + } + + _, err = b.WriteCAS(ctx, &pbresource.Resource{ + Id: nsID, + Generation: ulid.Make().String(), + Data: nsData, + Metadata: map[string]string{ + "generated_at": time.Now().Format(time.RFC3339), + }, + }) + + if err != nil { + return fmt.Errorf("failed to create the %q namespace: %v", resource.DefaultNamespaceName, err) + } + } + s.logger.Info("Created", "namespace", resource.DefaultNamespaceName) + return nil +} diff --git a/agent/consul/leader_ce.go b/agent/consul/leader_ce.go new file mode 100644 index 0000000000..2d67b7bded --- /dev/null +++ b/agent/consul/leader_ce.go @@ -0,0 +1,17 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !consulent + +package consul + +import ( + "context" + + "github.com/hashicorp/consul/internal/storage" +) + +func (s *Server) createDefaultPartition(ctx context.Context, b storage.Backend) error { + // no-op + return nil +} diff --git a/agent/consul/leader_ce_test.go b/agent/consul/leader_ce_test.go index 8988adb9f7..23009cfbc3 100644 --- a/agent/consul/leader_ce_test.go +++ b/agent/consul/leader_ce_test.go @@ -5,7 +5,19 @@ package consul -import libserf "github.com/hashicorp/consul/lib/serf" +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/storage" + libserf "github.com/hashicorp/consul/lib/serf" + "github.com/hashicorp/consul/proto-public/pbresource" + pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1" + "github.com/hashicorp/consul/testrpc" +) func updateSerfTags(s *Server, key, value string) { libserf.UpdateTag(s.serfLAN, key, value) @@ -14,3 +26,41 @@ func updateSerfTags(s *Server, key, value string) { libserf.UpdateTag(s.serfWAN, key, value) } } + +func TestServer_InitTenancy(t *testing.T) { + t.Parallel() + + _, conf := testServerConfig(t) + deps := newDefaultDeps(t, conf) + deps.Experiments = []string{"v2tenancy"} + deps.Registry = NewTypeRegistry() + + s, err := newServerWithDeps(t, conf, deps) + require.NoError(t, err) + + // first initTenancy call happens here + waitForLeaderEstablishment(t, s) + testrpc.WaitForLeader(t, s.RPC, "dc1") + + nsID := &pbresource.ID{ + Type: pbtenancy.NamespaceType, + Tenancy: resource.DefaultPartitionedTenancy(), + Name: resource.DefaultNamespaceName, + } + + ns, err := s.resourceServiceServer.Backend.Read(context.Background(), storage.StrongConsistency, nsID) + require.NoError(t, err) + require.Equal(t, resource.DefaultNamespaceName, ns.Id.Name) + + // explicitly call initiTenancy to verify we do not re-create namespace + err = s.initTenancy(context.Background(), s.resourceServiceServer.Backend) + require.NoError(t, err) + + // read again + actual, err := s.resourceServiceServer.Backend.Read(context.Background(), storage.StrongConsistency, nsID) + require.NoError(t, err) + + require.Equal(t, ns.Id.Uid, actual.Id.Uid) + require.Equal(t, ns.Generation, actual.Generation) + require.Equal(t, ns.Version, actual.Version) +} diff --git a/agent/consul/server.go b/agent/consul/server.go index 32829318bc..0e722f99f9 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -20,9 +20,13 @@ import ( "time" "github.com/armon/go-metrics" - "github.com/hashicorp/consul/internal/auth" - "github.com/hashicorp/consul/internal/mesh" - "github.com/hashicorp/consul/internal/multicluster" + "go.etcd.io/bbolt" + "golang.org/x/time/rate" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/reflection" + + "github.com/hashicorp/consul-net-rpc/net/rpc" "github.com/hashicorp/go-connlimit" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-memdb" @@ -33,13 +37,7 @@ import ( walmetrics "github.com/hashicorp/raft-wal/metrics" "github.com/hashicorp/raft-wal/verifier" "github.com/hashicorp/serf/serf" - "go.etcd.io/bbolt" - "golang.org/x/time/rate" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/reflection" - "github.com/hashicorp/consul-net-rpc/net/rpc" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl/resolver" "github.com/hashicorp/consul/agent/blockingquery" @@ -73,9 +71,12 @@ import ( "github.com/hashicorp/consul/agent/rpc/peering" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/token" + "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh" proxysnapshot "github.com/hashicorp/consul/internal/mesh/proxy-snapshot" + "github.com/hashicorp/consul/internal/multicluster" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/demo" "github.com/hashicorp/consul/internal/resource/reaper" diff --git a/agent/consul/server_ce.go b/agent/consul/server_ce.go index fb3e55e765..fd554ca2c3 100644 --- a/agent/consul/server_ce.go +++ b/agent/consul/server_ce.go @@ -11,10 +11,11 @@ import ( "time" "github.com/armon/go-metrics" + "google.golang.org/grpc" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/serf/coordinate" "github.com/hashicorp/serf/serf" - "google.golang.org/grpc" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/consul/reporting"