From a2aea97263c787de81f911e085cf81f56dfd0d82 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 21 Jul 2022 18:37:45 +0400 Subject: [PATCH] fix: write etcd PKI files in a controller Instead of writing PKI "once" around the startup time, keep writing PKI files as the certificates get updated. `etcd` is able to reload certificates, so we should keep updating them e.g. if the hostname/IPs change over time. Signed-off-by: Andrey Smirnov --- Dockerfile | 3 +- .../app/machined/pkg/controllers/etcd/etcd.go | 6 + .../app/machined/pkg/controllers/etcd/pki.go | 148 ++++++++++++++++++ .../runtime/v1alpha2/v1alpha2_controller.go | 2 + .../pkg/runtime/v1alpha2/v1alpha2_state.go | 3 + .../app/machined/pkg/system/services/etcd.go | 109 ++----------- .../app/machined/pkg/system/services/utils.go | 17 -- pkg/filetree/chown.go | 27 ++++ pkg/filetree/filetree.go | 6 + .../resources/cluster/deep_copy.generated.go | 22 +-- .../resources/etcd/deep_copy.generated.go | 13 ++ pkg/machinery/resources/etcd/etcd.go | 13 ++ pkg/machinery/resources/etcd/etcd_test.go | 32 ++++ pkg/machinery/resources/etcd/pki_status.go | 56 +++++++ .../resources/hardware/deep_copy.generated.go | 14 +- pkg/machinery/resources/hardware/hardware.go | 2 +- .../resources/secrets/deep_copy.generated.go | 8 + 17 files changed, 345 insertions(+), 136 deletions(-) create mode 100644 internal/app/machined/pkg/controllers/etcd/etcd.go create mode 100644 internal/app/machined/pkg/controllers/etcd/pki.go create mode 100644 pkg/filetree/chown.go create mode 100644 pkg/filetree/filetree.go create mode 100644 pkg/machinery/resources/etcd/deep_copy.generated.go create mode 100644 pkg/machinery/resources/etcd/etcd.go create mode 100644 pkg/machinery/resources/etcd/etcd_test.go create mode 100644 pkg/machinery/resources/etcd/pki_status.go diff --git a/Dockerfile b/Dockerfile index e54f1121f..f01f77d80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -253,8 +253,7 @@ COPY --from=generate-build /api/storage/*.pb.go /pkg/machinery/api/storage/ COPY --from=generate-build /api/resource/*.pb.go /pkg/machinery/api/resource/ COPY --from=generate-build /api/resource/secrets/*.pb.go /pkg/machinery/api/resource/secrets/ COPY --from=generate-build /api/inspect/*.pb.go /pkg/machinery/api/inspect/ -COPY --from=go-generate /src/pkg/machinery/resources/kubespan/ /pkg/machinery/resources/kubespan/ -COPY --from=go-generate /src/pkg/machinery/resources/network/ /pkg/machinery/resources/network/ +COPY --from=go-generate /src/pkg/machinery/resources/ /pkg/machinery/resources/ COPY --from=go-generate /src/pkg/machinery/config/types/v1alpha1/ /pkg/machinery/config/types/v1alpha1/ COPY --from=go-generate /src/pkg/machinery/nethelpers/ /pkg/machinery/nethelpers/ COPY --from=go-generate /src/pkg/machinery/extensions/ /pkg/machinery/extensions/ diff --git a/internal/app/machined/pkg/controllers/etcd/etcd.go b/internal/app/machined/pkg/controllers/etcd/etcd.go new file mode 100644 index 000000000..0c3a69e65 --- /dev/null +++ b/internal/app/machined/pkg/controllers/etcd/etcd.go @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package etcd provides controllers which manage etcd resources. +package etcd diff --git a/internal/app/machined/pkg/controllers/etcd/pki.go b/internal/app/machined/pkg/controllers/etcd/pki.go new file mode 100644 index 000000000..08f2ed074 --- /dev/null +++ b/internal/app/machined/pkg/controllers/etcd/pki.go @@ -0,0 +1,148 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package etcd + +import ( + "context" + "fmt" + "os" + + "github.com/cosi-project/runtime/pkg/controller" + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/safe" + "github.com/cosi-project/runtime/pkg/state" + "github.com/siderolabs/go-pointer" + "github.com/talos-systems/crypto/x509" + "go.uber.org/zap" + + "github.com/talos-systems/talos/pkg/filetree" + "github.com/talos-systems/talos/pkg/machinery/constants" + "github.com/talos-systems/talos/pkg/machinery/resources/etcd" + "github.com/talos-systems/talos/pkg/machinery/resources/secrets" +) + +// PKIController renders manifests based on templates and config/secrets. +type PKIController struct{} + +// Name implements controller.Controller interface. +func (ctrl *PKIController) Name() string { + return "etcd.PKIController" +} + +// Inputs implements controller.Controller interface. +func (ctrl *PKIController) Inputs() []controller.Input { + return []controller.Input{ + { + Namespace: secrets.NamespaceName, + Type: secrets.EtcdRootType, + ID: pointer.To(secrets.EtcdRootID), + Kind: controller.InputWeak, + }, + { + Namespace: secrets.NamespaceName, + Type: secrets.EtcdType, + ID: pointer.To(secrets.EtcdID), + Kind: controller.InputWeak, + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *PKIController) Outputs() []controller.Output { + return []controller.Output{ + { + Type: etcd.PKIStatusType, + Kind: controller.OutputExclusive, + }, + } +} + +// Run implements controller.Controller interface. +// +//nolint:gocyclo +func (ctrl *PKIController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { + for { + select { + case <-ctx.Done(): + return nil + case <-r.EventCh(): + } + + rootScrts, err := safe.ReaderGet[*secrets.EtcdRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdRootType, secrets.EtcdRootID, resource.VersionUndefined)) + if err != nil { + if state.IsNotFoundError(err) { + continue + } + + return fmt.Errorf("error getting root secrets: %w", err) + } + + scrts, err := safe.ReaderGet[*secrets.Etcd](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdType, secrets.EtcdID, resource.VersionUndefined)) + if err != nil { + if state.IsNotFoundError(err) { + continue + } + + return fmt.Errorf("error getting secrets: %w", err) + } + + if err = os.MkdirAll(constants.EtcdPKIPath, 0o700); err != nil { + return err + } + + if err = os.WriteFile(constants.KubernetesEtcdCACert, rootScrts.TypedSpec().EtcdCA.Crt, 0o400); err != nil { + return fmt.Errorf("failed to write CA certificate: %w", err) + } + + if err = os.WriteFile(constants.KubernetesEtcdCAKey, rootScrts.TypedSpec().EtcdCA.Key, 0o400); err != nil { + return fmt.Errorf("failed to write CA key: %w", err) + } + + etcdCerts := scrts.TypedSpec() + + for _, keypair := range []struct { + getter func() *x509.PEMEncodedCertificateAndKey + keyPath string + certPath string + }{ + { + getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.Etcd }, + keyPath: constants.KubernetesEtcdKey, + certPath: constants.KubernetesEtcdCert, + }, + { + getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdPeer }, + keyPath: constants.KubernetesEtcdPeerKey, + certPath: constants.KubernetesEtcdPeerCert, + }, + { + getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdAdmin }, + keyPath: constants.KubernetesEtcdAdminKey, + certPath: constants.KubernetesEtcdAdminCert, + }, + } { + if err = os.WriteFile(keypair.keyPath, keypair.getter().Key, 0o400); err != nil { + return err + } + + if err = os.WriteFile(keypair.certPath, keypair.getter().Crt, 0o400); err != nil { + return err + } + } + + if err = filetree.ChownRecursive(constants.EtcdPKIPath, constants.EtcdUserID, constants.EtcdUserID); err != nil { + return nil + } + + if err = safe.WriterModify(ctx, r, etcd.NewPKIStatus(etcd.NamespaceName, etcd.PKIID), func(status *etcd.PKIStatus) error { + status.TypedSpec().Ready = true + status.TypedSpec().Version = scrts.Metadata().Version().String() + + return nil + }); err != nil { + return fmt.Errorf("error updating PKI status: %w", err) + } + } +} diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index 7d65f90c3..aefdbcb4f 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -21,6 +21,7 @@ import ( "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/cluster" "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/config" + "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/etcd" "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/files" "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/hardware" "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/k8s" @@ -103,6 +104,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &config.MachineTypeController{}, &config.K8sAddressFilterController{}, &config.K8sControlPlaneController{}, + &etcd.PKIController{}, &files.CRIConfigPartsController{}, &files.CRIRegistryConfigController{}, &files.EtcFileController{ diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go index c9f81e63f..99cb91d20 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go @@ -16,6 +16,7 @@ import ( talosconfig "github.com/talos-systems/talos/pkg/machinery/config" "github.com/talos-systems/talos/pkg/machinery/resources/cluster" "github.com/talos-systems/talos/pkg/machinery/resources/config" + "github.com/talos-systems/talos/pkg/machinery/resources/etcd" "github.com/talos-systems/talos/pkg/machinery/resources/files" "github.com/talos-systems/talos/pkg/machinery/resources/hardware" "github.com/talos-systems/talos/pkg/machinery/resources/k8s" @@ -63,6 +64,7 @@ func NewState() (*State, error) { {cluster.NamespaceName, "Cluster configuration and discovery resources."}, {cluster.RawNamespaceName, "Cluster unmerged raw resources."}, {config.NamespaceName, "Talos node configuration."}, + {etcd.NamespaceName, "etcd resources."}, {files.NamespaceName, "Files and file-like resources."}, {hardware.NamespaceName, "Hardware resources."}, {k8s.NamespaceName, "Kubernetes all node types resources."}, @@ -87,6 +89,7 @@ func NewState() (*State, error) { &cluster.Member{}, &config.MachineConfig{}, &config.MachineType{}, + &etcd.PKIStatus{}, &files.EtcFileSpec{}, &files.EtcFileStatus{}, &hardware.Processor{}, diff --git a/internal/app/machined/pkg/system/services/etcd.go b/internal/app/machined/pkg/system/services/etcd.go index fdad7b944..5cbbaa8cf 100644 --- a/internal/app/machined/pkg/system/services/etcd.go +++ b/internal/app/machined/pkg/system/services/etcd.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "os" goruntime "runtime" @@ -23,7 +22,6 @@ import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/talos-systems/crypto/x509" "github.com/talos-systems/go-retry/retry" "github.com/talos-systems/net" clientv3 "go.etcd.io/etcd/client/v3" @@ -42,13 +40,14 @@ import ( "github.com/talos-systems/talos/internal/pkg/etcd" "github.com/talos-systems/talos/pkg/argsbuilder" "github.com/talos-systems/talos/pkg/conditions" + "github.com/talos-systems/talos/pkg/filetree" "github.com/talos-systems/talos/pkg/logging" machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine" "github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine" "github.com/talos-systems/talos/pkg/machinery/constants" + etcdresource "github.com/talos-systems/talos/pkg/machinery/resources/etcd" "github.com/talos-systems/talos/pkg/machinery/resources/k8s" "github.com/talos-systems/talos/pkg/machinery/resources/network" - "github.com/talos-systems/talos/pkg/machinery/resources/secrets" timeresource "github.com/talos-systems/talos/pkg/machinery/resources/time" ) @@ -87,7 +86,7 @@ func (e *Etcd) PreFunc(ctx context.Context, r runtime.Runtime) (err error) { } // Make sure etcd user can access files in the data directory. - if err = chownRecursive(constants.EtcdDataPath, constants.EtcdUserID, constants.EtcdUserID); err != nil { + if err = filetree.ChownRecursive(constants.EtcdDataPath, constants.EtcdUserID, constants.EtcdUserID); err != nil { return err } @@ -126,7 +125,7 @@ func (e *Etcd) PreFunc(ctx context.Context, r runtime.Runtime) (err error) { panic(fmt.Sprintf("unexpected machine type %v", t)) } - if err = generatePKI(ctx, r); err != nil { + if err = waitPKI(ctx, r); err != nil { return fmt.Errorf("failed to generate etcd PKI: %w", err) } @@ -243,102 +242,16 @@ func (e *Etcd) HealthSettings(runtime.Runtime) *health.Settings { } } -//nolint:gocyclo -func generatePKI(ctx context.Context, r runtime.Runtime) (err error) { - if err = os.MkdirAll(constants.EtcdPKIPath, 0o700); err != nil { - return err - } +func waitPKI(ctx context.Context, r runtime.Runtime) error { + _, err := r.State().V1Alpha2().Resources().WatchFor(ctx, + resource.NewMetadata(etcdresource.NamespaceName, etcdresource.PKIStatusType, etcdresource.PKIID, resource.VersionUndefined), + state.WithEventTypes(state.Created, state.Updated), + ) - if err = ioutil.WriteFile(constants.KubernetesEtcdCACert, r.Config().Cluster().Etcd().CA().Crt, 0o400); err != nil { - return fmt.Errorf("failed to write CA certificate: %w", err) - } - - if err = ioutil.WriteFile(constants.KubernetesEtcdCAKey, r.Config().Cluster().Etcd().CA().Key, 0o400); err != nil { - return fmt.Errorf("failed to write CA key: %w", err) - } - - // wait for etcd certificates to be generated in the controller - watchCh := make(chan state.Event) - - if err = r.State().V1Alpha2().Resources().Watch(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdType, secrets.EtcdID, resource.VersionUndefined), watchCh); err != nil { - return err - } - - var event state.Event - - for { - select { - case <-ctx.Done(): - return ctx.Err() - case event = <-watchCh: - } - - if event.Type == state.Created || event.Type == state.Updated { - break - } - } - - // wait for additional events with 500ms timeout and absorb new versions of the resource - const settleDownTimeout = 500 * time.Millisecond - - timer := time.NewTimer(settleDownTimeout) - defer timer.Stop() - -waitLoop: - for { - select { - case event = <-watchCh: - if !timer.Stop() { - <-timer.C - } - - timer.Reset(settleDownTimeout) - case <-timer.C: - break waitLoop - } - } - - etcdCerts := event.Resource.(*secrets.Etcd).TypedSpec() - - for _, keypair := range []struct { - getter func() *x509.PEMEncodedCertificateAndKey - keyPath string - certPath string - }{ - { - getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.Etcd }, - keyPath: constants.KubernetesEtcdKey, - certPath: constants.KubernetesEtcdCert, - }, - { - getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdPeer }, - keyPath: constants.KubernetesEtcdPeerKey, - certPath: constants.KubernetesEtcdPeerCert, - }, - { - getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdAdmin }, - keyPath: constants.KubernetesEtcdAdminKey, - certPath: constants.KubernetesEtcdAdminCert, - }, - } { - if err = ioutil.WriteFile(keypair.keyPath, keypair.getter().Key, 0o400); err != nil { - return err - } - - if err = ioutil.WriteFile(keypair.certPath, keypair.getter().Crt, 0o400); err != nil { - return err - } - } - - return chownRecursive(constants.EtcdPKIPath, constants.EtcdUserID, constants.EtcdUserID) + return err } func addMember(ctx context.Context, r runtime.Runtime, addrs []string, name string) (*clientv3.MemberListResponse, uint64, error) { - // update PKI on each join attempt - if err := generatePKI(ctx, r); err != nil { - return nil, 0, fmt.Errorf("failed to generate etcd PKI: %w", err) - } - client, err := etcd.NewClientFromControlPlaneIPsNoDiscovery(ctx, r.State().V1Alpha2().Resources()) if err != nil { return nil, 0, err @@ -672,7 +585,7 @@ func (e *Etcd) recoverFromSnapshot(hostname, primaryAddr string) error { return fmt.Errorf("error deleting snapshot: %w", err) } - return chownRecursive(constants.EtcdDataPath, constants.EtcdUserID, constants.EtcdUserID) + return filetree.ChownRecursive(constants.EtcdDataPath, constants.EtcdUserID, constants.EtcdUserID) } func promoteMember(ctx context.Context, r runtime.Runtime, memberID uint64) error { diff --git a/internal/app/machined/pkg/system/services/utils.go b/internal/app/machined/pkg/system/services/utils.go index 611beb353..14adc041f 100644 --- a/internal/app/machined/pkg/system/services/utils.go +++ b/internal/app/machined/pkg/system/services/utils.go @@ -6,11 +6,9 @@ package services import ( "fmt" - "io/fs" "io/ioutil" "os" "path/filepath" - "syscall" "golang.org/x/sys/unix" @@ -37,18 +35,3 @@ func prepareRootfs(id string) error { return nil } - -// chownRecursive changes file ownership recursively from the specified root. -func chownRecursive(root string, uid, gid uint32) error { - return filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { - if err != nil { - return err - } - - if info.Sys().(*syscall.Stat_t).Uid != uid || info.Sys().(*syscall.Stat_t).Gid != gid { - return os.Chown(path, int(uid), int(gid)) - } - - return nil - }) -} diff --git a/pkg/filetree/chown.go b/pkg/filetree/chown.go new file mode 100644 index 000000000..4858e1174 --- /dev/null +++ b/pkg/filetree/chown.go @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package filetree + +import ( + "io/fs" + "os" + "path/filepath" + "syscall" +) + +// ChownRecursive changes file ownership recursively from the specified root. +func ChownRecursive(root string, uid, gid uint32) error { + return filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + if info.Sys().(*syscall.Stat_t).Uid != uid || info.Sys().(*syscall.Stat_t).Gid != gid { + return os.Chown(path, int(uid), int(gid)) + } + + return nil + }) +} diff --git a/pkg/filetree/filetree.go b/pkg/filetree/filetree.go new file mode 100644 index 000000000..71898e336 --- /dev/null +++ b/pkg/filetree/filetree.go @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package filetree provides file tree operations. +package filetree diff --git a/pkg/machinery/resources/cluster/deep_copy.generated.go b/pkg/machinery/resources/cluster/deep_copy.generated.go index ec719c19c..332c56ebc 100644 --- a/pkg/machinery/resources/cluster/deep_copy.generated.go +++ b/pkg/machinery/resources/cluster/deep_copy.generated.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Code generated by "deep-copy -type AffiliateSpec -type IdentitySpec -type MemberSpec -type ConfigSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. +// Code generated by "deep-copy -type AffiliateSpec -type ConfigSpec -type IdentitySpec -type MemberSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package cluster @@ -28,6 +28,16 @@ func (o AffiliateSpec) DeepCopy() AffiliateSpec { return cp } +// DeepCopy generates a deep copy of ConfigSpec. +func (o ConfigSpec) DeepCopy() ConfigSpec { + var cp ConfigSpec = o + if o.ServiceEncryptionKey != nil { + cp.ServiceEncryptionKey = make([]byte, len(o.ServiceEncryptionKey)) + copy(cp.ServiceEncryptionKey, o.ServiceEncryptionKey) + } + return cp +} + // DeepCopy generates a deep copy of IdentitySpec. func (o IdentitySpec) DeepCopy() IdentitySpec { var cp IdentitySpec = o @@ -43,13 +53,3 @@ func (o MemberSpec) DeepCopy() MemberSpec { } return cp } - -// DeepCopy generates a deep copy of ConfigSpec. -func (o ConfigSpec) DeepCopy() ConfigSpec { - var cp ConfigSpec = o - if o.ServiceEncryptionKey != nil { - cp.ServiceEncryptionKey = make([]byte, len(o.ServiceEncryptionKey)) - copy(cp.ServiceEncryptionKey, o.ServiceEncryptionKey) - } - return cp -} diff --git a/pkg/machinery/resources/etcd/deep_copy.generated.go b/pkg/machinery/resources/etcd/deep_copy.generated.go new file mode 100644 index 000000000..547f8c27c --- /dev/null +++ b/pkg/machinery/resources/etcd/deep_copy.generated.go @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Code generated by "deep-copy -type PKIStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. + +package etcd + +// DeepCopy generates a deep copy of PKIStatusSpec. +func (o PKIStatusSpec) DeepCopy() PKIStatusSpec { + var cp PKIStatusSpec = o + return cp +} diff --git a/pkg/machinery/resources/etcd/etcd.go b/pkg/machinery/resources/etcd/etcd.go new file mode 100644 index 000000000..bdd4ca0de --- /dev/null +++ b/pkg/machinery/resources/etcd/etcd.go @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package etcd provides resources which interface with etcd. +package etcd + +import "github.com/cosi-project/runtime/pkg/resource" + +//go:generate deep-copy -type PKIStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . + +// NamespaceName contains resources supporting etcd service. +const NamespaceName resource.Namespace = "etcd" diff --git a/pkg/machinery/resources/etcd/etcd_test.go b/pkg/machinery/resources/etcd/etcd_test.go new file mode 100644 index 000000000..f79939c85 --- /dev/null +++ b/pkg/machinery/resources/etcd/etcd_test.go @@ -0,0 +1,32 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package etcd_test + +import ( + "context" + "testing" + + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/state" + "github.com/cosi-project/runtime/pkg/state/impl/inmem" + "github.com/cosi-project/runtime/pkg/state/impl/namespaced" + "github.com/cosi-project/runtime/pkg/state/registry" + "github.com/stretchr/testify/assert" + + "github.com/talos-systems/talos/pkg/machinery/resources/etcd" +) + +func TestRegisterResource(t *testing.T) { + ctx := context.TODO() + + resources := state.WrapCore(namespaced.NewState(inmem.Build)) + resourceRegistry := registry.NewResourceRegistry(resources) + + for _, resource := range []resource.Resource{ + &etcd.PKIStatus{}, + } { + assert.NoError(t, resourceRegistry.Register(ctx, resource)) + } +} diff --git a/pkg/machinery/resources/etcd/pki_status.go b/pkg/machinery/resources/etcd/pki_status.go new file mode 100644 index 000000000..14f0f8eca --- /dev/null +++ b/pkg/machinery/resources/etcd/pki_status.go @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package etcd + +import ( + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/resource/meta" + "github.com/cosi-project/runtime/pkg/resource/typed" +) + +// PKIStatusType is type of PKIStatus resource. +const PKIStatusType = resource.Type("PKIStatuses.etcd.talos.dev") + +// PKIID is resource ID for PKIStatus resource for etcd. +const PKIID = resource.ID("etcd") + +// PKIStatus resource holds status of rendered secrets. +type PKIStatus = typed.Resource[PKIStatusSpec, PKIStatusRD] + +// PKIStatusSpec describes status of rendered secrets. +type PKIStatusSpec struct { + Ready bool `yaml:"ready"` + Version string `yaml:"version"` +} + +// NewPKIStatus initializes a PKIStatus resource. +func NewPKIStatus(namespace resource.Namespace, id resource.ID) *PKIStatus { + return typed.NewResource[PKIStatusSpec, PKIStatusRD]( + resource.NewMetadata(namespace, PKIStatusType, id, resource.VersionUndefined), + PKIStatusSpec{}, + ) +} + +// PKIStatusRD provides auxiliary methods for PKIStatus. +type PKIStatusRD struct{} + +// ResourceDefinition implements typed.ResourceDefinition interface. +func (PKIStatusRD) ResourceDefinition(resource.Metadata, PKIStatusSpec) meta.ResourceDefinitionSpec { + return meta.ResourceDefinitionSpec{ + Type: PKIStatusType, + Aliases: []resource.Type{}, + DefaultNamespace: NamespaceName, + PrintColumns: []meta.PrintColumn{ + { + Name: "Ready", + JSONPath: "{.ready}", + }, + { + Name: "Secrets Version", + JSONPath: "{.version}", + }, + }, + } +} diff --git a/pkg/machinery/resources/hardware/deep_copy.generated.go b/pkg/machinery/resources/hardware/deep_copy.generated.go index 2efb0e9e1..5bc27170d 100644 --- a/pkg/machinery/resources/hardware/deep_copy.generated.go +++ b/pkg/machinery/resources/hardware/deep_copy.generated.go @@ -2,22 +2,22 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Code generated by "deep-copy -type ProcessorSpec -type MemoryModuleSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. +// Code generated by "deep-copy -type MemoryModuleSpec -type ProcessorSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package hardware -// DeepCopy generates a deep copy of ProcessorSpec. -func (o ProcessorSpec) DeepCopy() ProcessorSpec { - var cp ProcessorSpec = o - return cp -} - // DeepCopy generates a deep copy of MemoryModuleSpec. func (o MemoryModuleSpec) DeepCopy() MemoryModuleSpec { var cp MemoryModuleSpec = o return cp } +// DeepCopy generates a deep copy of ProcessorSpec. +func (o ProcessorSpec) DeepCopy() ProcessorSpec { + var cp ProcessorSpec = o + return cp +} + // DeepCopy generates a deep copy of SystemInformationSpec. func (o SystemInformationSpec) DeepCopy() SystemInformationSpec { var cp SystemInformationSpec = o diff --git a/pkg/machinery/resources/hardware/hardware.go b/pkg/machinery/resources/hardware/hardware.go index 9d59b8634..dcd783e7c 100644 --- a/pkg/machinery/resources/hardware/hardware.go +++ b/pkg/machinery/resources/hardware/hardware.go @@ -8,7 +8,7 @@ import ( "github.com/cosi-project/runtime/pkg/resource" ) -//go:generate deep-copy -type ProcessorSpec -type MemoryModuleSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . +//go:generate deep-copy -type MemoryModuleSpec -type ProcessorSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains resources related to hardware as a whole. const NamespaceName resource.Namespace = "hardware" diff --git a/pkg/machinery/resources/secrets/deep_copy.generated.go b/pkg/machinery/resources/secrets/deep_copy.generated.go index d3386e342..736601516 100644 --- a/pkg/machinery/resources/secrets/deep_copy.generated.go +++ b/pkg/machinery/resources/secrets/deep_copy.generated.go @@ -112,6 +112,14 @@ func (o KubernetesRootSpec) DeepCopy() KubernetesRootSpec { *cp.Endpoint.User = *o.Endpoint.User } } + if o.LocalEndpoint != nil { + cp.LocalEndpoint = new(url.URL) + *cp.LocalEndpoint = *o.LocalEndpoint + if o.LocalEndpoint.User != nil { + cp.LocalEndpoint.User = new(url.Userinfo) + *cp.LocalEndpoint.User = *o.LocalEndpoint.User + } + } if o.CertSANs != nil { cp.CertSANs = make([]string, len(o.CertSANs)) copy(cp.CertSANs, o.CertSANs)