Skip to content

Commit

Permalink
chore: split config.Registry into the separate resource
Browse files Browse the repository at this point in the history
Required for #9614

Closes #9766

Signed-off-by: Dmitriy Matrenichev <[email protected]>
  • Loading branch information
DmitriyMV committed Nov 27, 2024
1 parent c735d14 commit ccc5a8d
Show file tree
Hide file tree
Showing 30 changed files with 2,686 additions and 128 deletions.
35 changes: 35 additions & 0 deletions api/resource/definitions/cri/cri.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package talos.resource.definitions.cri;
option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cri";
option java_package = "dev.talos.api.resource.definitions.cri";

import "common/common.proto";
import "google/protobuf/struct.proto";
import "resource/definitions/enums/enums.proto";

Expand All @@ -14,6 +15,40 @@ message ImageCacheConfigSpec {
repeated string roots = 2;
}

// RegistriesConfigSpec describes status of rendered secrets.
message RegistriesConfigSpec {
map<string, RegistryMirrorConfig> registry_mirrors = 1;
map<string, RegistryConfig> registry_config = 2;
}

// RegistryAuthConfig specifies authentication configuration for a registry.
message RegistryAuthConfig {
string registry_username = 1;
string registry_password = 2;
string registry_auth = 3;
string registry_identity_token = 4;
}

// RegistryConfig specifies auth & TLS config per registry.
message RegistryConfig {
RegistryTLSConfig registry_tls = 1;
RegistryAuthConfig registry_auth = 2;
}

// RegistryMirrorConfig represents mirror configuration for a registry.
message RegistryMirrorConfig {
repeated string mirror_endpoints = 1;
bool mirror_override_path = 2;
bool mirror_skip_fallback = 3;
}

// RegistryTLSConfig specifies TLS config for HTTPS registries.
message RegistryTLSConfig {
common.PEMEncodedCertificateAndKey tls_client_identity = 1;
bytes tlsca = 2;
bool tls_insecure_skip_verify = 3;
}

// SeccompProfileSpec represents the SeccompProfile.
message SeccompProfileSpec {
string name = 1;
Expand Down
6 changes: 6 additions & 0 deletions hack/structprotogen/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ func formatTypeName(fieldTypePkg string, fieldType string, declPkg string) (stri
return commoProto, "common.PEMEncodedCertificate"
case typeData{"github.com/siderolabs/talos/pkg/machinery/cel", "Expression"}:
return "google/api/expr/v1alpha1/checked.proto", "google.api.expr.v1alpha1.CheckedExpr"
case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/cri", "RegistryMirrorConfig"}:
// This is a hack, but I (Dmitry) don't have enough patience to figure out why we don't support complex maps
return "resource/definitions/cri/registry.proto", "talos.resource.definitions.cri.RegistryMirrorConfig"
case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/cri", "RegistryConfig"}:
// This is a hack, but I (Dmitry) don't have enough patience to figure out why we don't support complex maps
return "resource/definitions/cri/registry.proto", "talos.resource.definitions.cri.RegistryConfig"
default:
return "", ""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/siderolabs/talos/pkg/machinery/api/common"
"github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/cri"
)

func containerdNamespaceHelper(ctx context.Context, ns common.ContainerdNamespace) (context.Context, error) {
Expand Down Expand Up @@ -91,7 +92,7 @@ func (s *Server) ImagePull(ctx context.Context, req *machine.ImagePullRequest) (
return nil, err
}

_, err = image.Pull(ctx, s.Controller.Runtime().Config().Machine().Registries(), client, req.Reference, image.WithSkipIfAlreadyPulled())
_, err = image.Pull(ctx, cri.RegistryBuilder(s.Controller.Runtime().State().V1Alpha2().Resources()), client, req.Reference, image.WithSkipIfAlreadyPulled())
if err != nil {
if errdefs.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "error pulling image: %s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import (
"github.com/siderolabs/talos/pkg/machinery/meta"
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
crires "github.com/siderolabs/talos/pkg/machinery/resources/cri"
etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time"
Expand Down Expand Up @@ -482,7 +483,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*mach

log.Printf("validating %q", in.GetImage())

if err := install.PullAndValidateInstallerImage(ctx, s.Controller.Runtime().Config().Machine().Registries(), in.GetImage()); err != nil {
if err := install.PullAndValidateInstallerImage(ctx, crires.RegistryBuilder(s.Controller.Runtime().State().V1Alpha2().Resources()), in.GetImage()); err != nil {
return nil, fmt.Errorf("error validating installer image %q: %w", in.GetImage(), err)
}

Expand Down
118 changes: 118 additions & 0 deletions internal/app/machined/pkg/controllers/cri/registries_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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 cri

import (
"context"
"fmt"

"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/controller/generic/transform"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/optional"
"go.uber.org/zap"

"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/cri"
)

// RegistriesConfigController watches v1alpha1.Config, updates registry.RegistriesConfig.
type RegistriesConfigController = transform.Controller[*config.MachineConfig, *cri.RegistriesConfig]

// NewRegistriesConfigController creates new config controller.
//
//nolint:gocyclo
func NewRegistriesConfigController() *RegistriesConfigController {
return transform.NewController(
transform.Settings[*config.MachineConfig, *cri.RegistriesConfig]{
Name: "cri.RegistriesConfigController",
MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*cri.RegistriesConfig] {
if cfg.Metadata().ID() != config.V1Alpha1ID {
return optional.None[*cri.RegistriesConfig]()
}

return optional.Some(cri.NewRegistriesConfig())
},
TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *cri.RegistriesConfig) error {
imageCacheConfig, err := safe.ReaderGetByID[*cri.ImageCacheConfig](ctx, r, cri.ImageCacheConfigID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("failed to get image cache config: %w", err)
}

spec := res.TypedSpec()

spec.RegistryConfig = clearInit(spec.RegistryConfig)
spec.RegistryMirrors = clearInit(spec.RegistryMirrors)

if cfg != nil && cfg.Config().Machine() != nil {
// This is breaking our interface abstraction, but we need to get the underlying types for protobuf
// encoding to work correctly.
mr := cfg.Provider().RawV1Alpha1().MachineConfig.MachineRegistries

for k, v := range mr.RegistryConfig {
spec.RegistryConfig[k] = &cri.RegistryConfig{
RegistryTLS: &cri.RegistryTLSConfig{
TLSClientIdentity: v.RegistryTLS.TLSClientIdentity,
TLSCA: v.RegistryTLS.TLSCA,
TLSInsecureSkipVerify: v.RegistryTLS.TLSInsecureSkipVerify,
},
RegistryAuth: &cri.RegistryAuthConfig{
RegistryUsername: v.RegistryAuth.RegistryUsername,
RegistryPassword: v.RegistryAuth.RegistryPassword,
RegistryAuth: v.RegistryAuth.RegistryAuth,
RegistryIdentityToken: v.RegistryAuth.RegistryIdentityToken,
},
}
}

for k, v := range mr.RegistryMirrors {
spec.RegistryMirrors[k] = &cri.RegistryMirrorConfig{
MirrorEndpoints: v.MirrorEndpoints,
MirrorOverridePath: v.MirrorOverridePath,
MirrorSkipFallback: v.MirrorSkipFallback,
}
}
}

if imageCacheConfig != nil && imageCacheConfig.TypedSpec().Status == cri.ImageCacheStatusReady {
// if the '*' was configured, we just use it, otherwise create it so that we can inject the registryd
if _, hasStar := spec.RegistryMirrors["*"]; !hasStar {
spec.RegistryMirrors["*"] = &cri.RegistryMirrorConfig{}
}

// inject the registryd mirror endpoint as the first one for all registries
for registry := range spec.RegistryMirrors {
spec.RegistryMirrors[registry].MirrorEndpoints = append(
[]string{"http://" + constants.RegistrydListenAddress},
spec.RegistryMirrors[registry].MirrorEndpoints...,
)
}
}

return nil
},
},
transform.WithExtraInputs(
controller.Input{
Namespace: cri.NamespaceName,
Type: cri.ImageCacheConfigType,
ID: optional.Some(cri.ImageCacheConfigID),
Kind: controller.InputWeak,
},
),
)
}

func clearInit[M ~map[K]V, K comparable, V any](m M) M {
if m == nil {
return make(M)
}

clear(m)

return m
}
113 changes: 113 additions & 0 deletions internal/app/machined/pkg/controllers/cri/registries_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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 cri_test

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"

"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
crires "github.com/siderolabs/talos/pkg/machinery/resources/cri"
)

type ConfigSuite struct {
ctest.DefaultSuite
}

func (suite *ConfigSuite) TestRegistry() {
cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineType: "controlplane",
MachineRegistries: v1alpha1.RegistriesConfig{
RegistryMirrors: map[string]*v1alpha1.RegistryMirrorConfig{
"docker.io": {MirrorEndpoints: []string{"https://mirror.io"}},
},
},
},
}))

suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg))

ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) {
spec := r.TypedSpec()

a.Equal(
map[string]*crires.RegistryMirrorConfig{
"docker.io": {MirrorEndpoints: []string{"https://mirror.io"}},
},
spec.RegistryMirrors,
)
})

ic := crires.NewImageCacheConfig()
ic.TypedSpec().Roots = []string{"/imagecache"}
ic.TypedSpec().Status = crires.ImageCacheStatusReady

suite.Require().NoError(suite.State().Create(suite.Ctx(), ic))

ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) {
spec := r.TypedSpec()

a.Equal(
map[string]*crires.RegistryMirrorConfig{
"*": {MirrorEndpoints: []string{
"http://" + constants.RegistrydListenAddress,
}},
"docker.io": {MirrorEndpoints: []string{
"http://" + constants.RegistrydListenAddress,
"https://mirror.io",
}},
},
spec.RegistryMirrors,
)
})
}

func (suite *ConfigSuite) TestRegistryNoMachineConfig() {
cfg := config.NewMachineConfig(container.NewV1Alpha1(nil))

suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg))

ic := crires.NewImageCacheConfig()
ic.TypedSpec().Roots = []string{"/imagecache"}
ic.TypedSpec().Status = crires.ImageCacheStatusReady

suite.Require().NoError(suite.State().Create(suite.Ctx(), ic))

ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) {
spec := r.TypedSpec()

a.Equal(
map[string]*crires.RegistryMirrorConfig{
"*": {MirrorEndpoints: []string{
"http://" + constants.RegistrydListenAddress,
}},
},
spec.RegistryMirrors,
)
})
}

func TestConfigSuite(t *testing.T) {
t.Parallel()

suite.Run(t, &ConfigSuite{
DefaultSuite: ctest.DefaultSuite{
Timeout: 5 * time.Second,
AfterSetup: func(s *ctest.DefaultSuite) {
s.Require().NoError(s.Runtime().RegisterController(cri.NewRegistriesConfigController()))
},
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

"github.com/siderolabs/talos/internal/pkg/containers/cri/containerd"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/cri"
"github.com/siderolabs/talos/pkg/machinery/resources/files"
)

Expand All @@ -40,9 +40,9 @@ func (ctrl *CRIRegistryConfigController) Name() string {
func (ctrl *CRIRegistryConfigController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: config.NamespaceName,
Type: config.MachineConfigType,
ID: optional.Some(config.V1Alpha1ID),
Namespace: cri.NamespaceName,
Type: cri.RegistriesConfigType,
ID: optional.Some(cri.RegistriesConfigID),
Kind: controller.InputWeak,
},
}
Expand Down Expand Up @@ -88,23 +88,23 @@ func (ctrl *CRIRegistryConfigController) Run(ctx context.Context, r controller.R
case <-r.EventCh():
}

cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
cfg, err := safe.ReaderGetByID[*cri.RegistriesConfig](ctx, r, cri.RegistriesConfigID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
return fmt.Errorf("error getting registries config: %w", err)
}

var (
criRegistryContents []byte
criHosts *containerd.HostsConfig
)

if cfg != nil && cfg.Config().Machine() != nil {
criRegistryContents, err = containerd.GenerateCRIConfig(cfg.Config().Machine().Registries())
if cfg != nil {
criRegistryContents, err = containerd.GenerateCRIConfig(cfg.TypedSpec())
if err != nil {
return err
}

criHosts, err = containerd.GenerateHosts(cfg.Config().Machine().Registries(), basePath)
criHosts, err = containerd.GenerateHosts(cfg.TypedSpec(), basePath)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit ccc5a8d

Please sign in to comment.