Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: split config.Registries into the separate resource. #9780

Merged
merged 1 commit into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the author gave up 😛

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah...

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