Skip to content

Commit

Permalink
feat: implement inline manifests in the machine configuration
Browse files Browse the repository at this point in the history
Inline manifests work exactly same way as extra manifests, but the
manifest itself can be stored in the config body.

Example config patch:

```
--config-patch '[{"op": "replace", "path": "/cluster/inlineManifests", "value": [{"name": "foo", "contents": "apiVersion: v1\nkind: Namespace\nmetadata:\n  name: ci\n"}]}]'
```

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Apr 12, 2021
1 parent e26c977 commit 2402f20
Show file tree
Hide file tree
Showing 15 changed files with 508 additions and 19 deletions.
8 changes: 7 additions & 1 deletion hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ preface = """\
* Talos `system` services now run without container images on initramfs from the single executable; this change reduces RAM usage, initramfs size and boot time..
"""

[notes.machineconfig]
[notes.machineconfig-1]
title = "Install Disk Selector"
description = """\
Install section of the machine config now has `diskSelector` field that allows querying install disk using the list of qualifiers:
Expand All @@ -57,6 +57,12 @@ Install section of the machine config now has `diskSelector` field that allows q
```
`talosctl disks -n <node> -i` can be used to check allowed disk qualifiers when the node is running in the maintenance mode.
"""

[notes.machineconfig-2]
title = "Inline Kubernetes Manifests"
description = """\
* boostrap manifests can now be submitted in the configuration body using the `cluster.inlineManifests` field.
"""

[make_deps]
Expand Down
11 changes: 11 additions & 0 deletions internal/app/machined/pkg/controllers/config/k8s_control_plane.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,26 +245,37 @@ func (ctrl *K8sControlPlaneController) manageExtraManifestsConfig(ctx context.Co

for _, url := range cfgProvider.Cluster().Network().CNI().URLs() {
spec.ExtraManifests = append(spec.ExtraManifests, config.ExtraManifest{
Name: url,
URL: url,
Priority: "05", // push CNI to the top
})
}

for _, url := range cfgProvider.Cluster().ExternalCloudProvider().ManifestURLs() {
spec.ExtraManifests = append(spec.ExtraManifests, config.ExtraManifest{
Name: url,
URL: url,
Priority: "30", // after default manifests
})
}

for _, url := range cfgProvider.Cluster().ExtraManifestURLs() {
spec.ExtraManifests = append(spec.ExtraManifests, config.ExtraManifest{
Name: url,
URL: url,
Priority: "99", // make sure extra manifests come last, when PSP is already created
ExtraHeaders: cfgProvider.Cluster().ExtraManifestHeaderMap(),
})
}

for _, manifest := range cfgProvider.Cluster().InlineManifests() {
spec.ExtraManifests = append(spec.ExtraManifests, config.ExtraManifest{
Name: manifest.Name(),
Priority: "99", // make sure extra manifests come last, when PSP is already created
InlineManifest: manifest.Contents(),
})
}

r.(*config.K8sControlPlane).SetExtraManifests(spec)

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"log"
"net/url"
"reflect"
"strings"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -214,17 +215,62 @@ func (suite *K8sControlPlaneSuite) TestReconcileExternalCloudProvider() {
suite.Assert().Equal(config.K8sExtraManifestsSpec{
ExtraManifests: []config.ExtraManifest{
{
Name: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml",
URL: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml",
Priority: "30",
},
{
Name: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml",
URL: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml",
Priority: "30",
},
},
}, r.(*config.K8sControlPlane).ExtraManifests())
}

func (suite *K8sControlPlaneSuite) TestReconcileInlineManifests() {
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)

cfg := config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
},
ClusterInlineManifests: v1alpha1.ClusterInlineManifests{
{
InlineManifestName: "namespace-ci",
InlineManifestContents: strings.TrimSpace(`
apiVersion: v1
kind: Namespace
metadata:
name: ci
`),
},
},
},
})

suite.setupMachine(cfg)

r, err := suite.state.Get(suite.ctx, config.NewK8sExtraManifests().Metadata())
suite.Require().NoError(err)

suite.Assert().Equal(config.K8sExtraManifestsSpec{
ExtraManifests: []config.ExtraManifest{
{
Name: "namespace-ci",
Priority: "99",
InlineManifest: "apiVersion: v1\nkind: Namespace\nmetadata:\n\tname: ci",
},
},
}, r.(*config.K8sControlPlane).ExtraManifests())
}

func (suite *K8sControlPlaneSuite) TearDownTest() {
suite.T().Log("tear down")

Expand Down
32 changes: 28 additions & 4 deletions internal/app/machined/pkg/controllers/k8s/extra_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (ctrl *ExtraManifestController) Run(ctx context.Context, r controller.Runti
for _, manifest := range config.ExtraManifests {
var id resource.ID

id, err = ctrl.download(ctx, r, logger, manifest)
id, err = ctrl.process(ctx, r, logger, manifest)
if err != nil {
multiErr = multierror.Append(multiErr, err)
}
Expand Down Expand Up @@ -139,9 +139,18 @@ func (ctrl *ExtraManifestController) Run(ctx context.Context, r controller.Runti
}
}

func (ctrl *ExtraManifestController) download(ctx context.Context, r controller.Runtime, logger *log.Logger, manifest config.ExtraManifest) (id resource.ID, err error) {
id = fmt.Sprintf("%s-%s", manifest.Priority, manifest.URL)
func (ctrl *ExtraManifestController) process(ctx context.Context, r controller.Runtime, logger *log.Logger, manifest config.ExtraManifest) (id resource.ID, err error) {
id = fmt.Sprintf("%s-%s", manifest.Priority, manifest.Name)

// inline manifests don't require download
if manifest.InlineManifest != "" {
return id, ctrl.processInline(ctx, r, manifest, id)
}

return id, ctrl.processURL(ctx, r, logger, manifest, id)
}

func (ctrl *ExtraManifestController) processURL(ctx context.Context, r controller.Runtime, logger *log.Logger, manifest config.ExtraManifest, id resource.ID) (err error) {
var tmpDir string

tmpDir, err = ioutil.TempDir("", "talos")
Expand Down Expand Up @@ -204,7 +213,22 @@ func (ctrl *ExtraManifestController) download(ctx context.Context, r controller.
return
}

return id, nil
return nil
}

func (ctrl *ExtraManifestController) processInline(ctx context.Context, r controller.Runtime, manifest config.ExtraManifest, id resource.ID) error {
err := r.Modify(
ctx,
k8s.NewManifest(k8s.ControlPlaneNamespaceName, id),
func(r resource.Resource) error {
return r.(*k8s.Manifest).SetYAML([]byte(manifest.InlineManifest))
},
)
if err != nil {
return fmt.Errorf("error updating manifests: %w", err)
}

return nil
}

//nolint: dupl
Expand Down
153 changes: 153 additions & 0 deletions internal/app/machined/pkg/controllers/k8s/extra_manifest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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 k8s_test

import (
"context"
"fmt"
"log"
"reflect"
"strings"
"sync"
"testing"
"time"

"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-retry/retry"
"github.com/talos-systems/os-runtime/pkg/controller/runtime"
"github.com/talos-systems/os-runtime/pkg/resource"
"github.com/talos-systems/os-runtime/pkg/state"
"github.com/talos-systems/os-runtime/pkg/state/impl/inmem"
"github.com/talos-systems/os-runtime/pkg/state/impl/namespaced"

k8sctrl "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/k8s"
"github.com/talos-systems/talos/pkg/resources/config"
"github.com/talos-systems/talos/pkg/resources/k8s"
"github.com/talos-systems/talos/pkg/resources/v1alpha1"
)

type ExtraManifestSuite struct {
suite.Suite

state state.State

runtime *runtime.Runtime
wg sync.WaitGroup

ctx context.Context
ctxCancel context.CancelFunc
}

func (suite *ExtraManifestSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)

suite.state = state.WrapCore(namespaced.NewState(inmem.Build))

var err error

logger := log.New(log.Writer(), "controller-runtime: ", log.Flags())

suite.runtime, err = runtime.NewRuntime(suite.state, logger)
suite.Require().NoError(err)

suite.Require().NoError(suite.runtime.RegisterController(&k8sctrl.ExtraManifestController{}))

suite.startRuntime()
}

func (suite *ExtraManifestSuite) startRuntime() {
suite.wg.Add(1)

go func() {
defer suite.wg.Done()

suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}

//nolint:dupl
func (suite *ExtraManifestSuite) assertExtraManifests(manifests []string) error {
resources, err := suite.state.List(suite.ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined))
if err != nil {
return retry.UnexpectedError(err)
}

ids := make([]string, 0, len(resources.Items))

for _, res := range resources.Items {
ids = append(ids, res.Metadata().ID())
}

if !reflect.DeepEqual(manifests, ids) {
return retry.ExpectedError(fmt.Errorf("expected %q, got %q", manifests, ids))
}

return nil
}

func (suite *ExtraManifestSuite) TestReconcileInlineManifests() {
configExtraManifests := config.NewK8sExtraManifests()
configExtraManifests.SetExtraManifests(config.K8sExtraManifestsSpec{
ExtraManifests: []config.ExtraManifest{
{
Name: "namespaces",
Priority: "99",
InlineManifest: strings.TrimSpace(`
apiVersion: v1
kind: Namespace
metadata:
name: ci
---
apiVersion: v1
kind: Namespace
metadata:
name: build
`),
},
},
})

serviceNetworkd := v1alpha1.NewService("networkd")
serviceNetworkd.SetRunning(true)
serviceNetworkd.SetHealthy(true)

suite.Require().NoError(suite.state.Create(suite.ctx, configExtraManifests))
suite.Require().NoError(suite.state.Create(suite.ctx, serviceNetworkd))

suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertExtraManifests(
[]string{
"99-namespaces",
},
)
},
))

r, err := suite.state.Get(suite.ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "99-namespaces", resource.VersionUndefined))
suite.Require().NoError(err)

manifest := r.(*k8s.Manifest) //nolint:errcheck,forcetypeassert

suite.Assert().Len(manifest.Objects(), 2)
suite.Assert().Equal("ci", manifest.Objects()[0].GetName())
suite.Assert().Equal("build", manifest.Objects()[1].GetName())
}

func (suite *ExtraManifestSuite) TearDownTest() {
suite.T().Log("tear down")

suite.ctxCancel()

suite.wg.Wait()

// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), v1alpha1.NewService("foo")))
suite.Assert().NoError(suite.state.Create(context.Background(), config.NewK8sManifests()))
}

func TestExtraManifestSuite(t *testing.T) {
suite.Run(t, new(ExtraManifestSuite))
}
7 changes: 7 additions & 0 deletions pkg/machinery/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ type ClusterConfig interface {
ExternalCloudProvider() ExternalCloudProvider
ExtraManifestURLs() []string
ExtraManifestHeaderMap() map[string]string
InlineManifests() []InlineManifest
AdminKubeconfig() AdminKubeconfig
ScheduleOnMasters() bool
}
Expand Down Expand Up @@ -403,3 +404,9 @@ type VolumeMount interface {
MountPath() string
ReadOnly() bool
}

// InlineManifest describes inline manifest for the cluster boostrap.
type InlineManifest interface {
Name() string
Contents() string
}
1 change: 1 addition & 0 deletions pkg/machinery/config/types/v1alpha1/generate/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func initUd(in *Input) (*v1alpha1.Config, error) {
ClusterServiceAccount: in.Certs.K8sServiceAccount,
BootstrapToken: in.Secrets.BootstrapToken,
ClusterAESCBCEncryptionSecret: in.Secrets.AESCBCEncryptionSecret,
ClusterInlineManifests: v1alpha1.ClusterInlineManifests{},
}

config.MachineConfig = machine
Expand Down
11 changes: 11 additions & 0 deletions pkg/machinery/config/types/v1alpha1/v1alpha1_clusterconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ func (c *ClusterConfig) ExtraManifestHeaderMap() map[string]string {
return c.ExtraManifestHeaders
}

// InlineManifests implements the config.ClusterConfig interface.
func (c *ClusterConfig) InlineManifests() []config.InlineManifest {
manifests := make([]config.InlineManifest, len(c.ClusterInlineManifests))

for i := range manifests {
manifests[i] = c.ClusterInlineManifests[i]
}

return manifests
}

// AdminKubeconfig implements the config.ClusterConfig interface.
func (c *ClusterConfig) AdminKubeconfig() config.AdminKubeconfig {
if c.AdminKubeconfigConfig == nil {
Expand Down
15 changes: 15 additions & 0 deletions pkg/machinery/config/types/v1alpha1/v1alpha1_inlinemanifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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 v1alpha1

// Name implements the config.InlineManifest interface.
func (m ClusterInlineManifest) Name() string {
return m.InlineManifestName
}

// Contents implements the config.InlineManifest interface.
func (m ClusterInlineManifest) Contents() string {
return m.InlineManifestContents
}
Loading

0 comments on commit 2402f20

Please sign in to comment.