Skip to content

Commit

Permalink
Merge pull request #3209 from mikhail-sakhnov/windows-calico-host-con…
Browse files Browse the repository at this point in the history
…tainers

Windows calico host containers
  • Loading branch information
twz123 authored Sep 13, 2023
2 parents 3368d8e + ce2b760 commit 25ba1f5
Show file tree
Hide file tree
Showing 27 changed files with 597 additions and 538 deletions.
8 changes: 7 additions & 1 deletion cmd/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,14 @@ func (c *command) start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("failed to create calico_init manifests saver: %w", err)
}
windowsStackSaver, err := controller.NewManifestsSaver("windows", c.K0sVars.DataDir)
if err != nil {
return fmt.Errorf("failed to create windows manifests saver: %w", err)
}
clusterComponents.Add(ctx, controller.NewCalico(c.K0sVars, calicoInitSaver, calicoSaver))

if !slices.Contains(c.DisableComponents, constant.WindowsNodeComponentName) {
clusterComponents.Add(ctx, controller.NewWindowsStackComponent(c.K0sVars, adminClientFactory, windowsStackSaver))
}
kubeRouterSaver, err := controller.NewManifestsSaver("kuberouter", c.K0sVars.DataDir)
if err != nil {
return fmt.Errorf("failed to create kuberouter manifests saver: %w", err)
Expand Down
17 changes: 0 additions & 17 deletions cmd/worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,6 @@ func (c *Command) Start(ctx context.Context) error {
IPTablesMode: c.WorkerOptions.IPTablesMode,
})

if runtime.GOOS == "windows" {
if c.TokenArg == "" {
return fmt.Errorf("no join-token given, which is required for windows bootstrap")
}
componentManager.Add(ctx, &worker.KubeProxy{
K0sVars: c.K0sVars,
LogLevel: c.Logging["kube-proxy"],
CIDRRange: c.CIDRRange,
})
componentManager.Add(ctx, &worker.CalicoInstaller{
Token: c.TokenArg,
APIAddress: c.APIServer,
CIDRRange: c.CIDRRange,
ClusterDNS: c.ClusterDNS,
})
}

certManager := worker.NewCertificateManager(ctx, kubeletKubeconfigPath)

// if running inside a controller, status component is already running
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/avast/retry-go v3.0.0+incompatible
github.com/bombsimon/logrusr/v2 v2.0.1
github.com/carlmjohnson/requests v0.23.4
github.com/cavaliergopher/grab/v3 v3.0.1
github.com/cloudflare/cfssl v1.6.4
github.com/containerd/containerd v1.7.5
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuP
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/carlmjohnson/requests v0.23.4 h1:AxcvapfB9RPXLSyvAHk9YJoodQ43ZjzNHj6Ft3tQGdg=
github.com/carlmjohnson/requests v0.23.4/go.mod h1:Qzp6tW4DQyainPP+tGwiJTzwxvElTIKm0B191TgTtOA=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
Expand Down
5 changes: 0 additions & 5 deletions pkg/apis/k0s/v1beta1/calico.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ type Calico struct {

// The virtual network ID for VXLAN (default: 4096)
VxlanVNI int `json:"vxlanVNI"`

// Windows Nodes (default: false)
WithWindowsNodes bool `json:"withWindowsNodes"`
}

// DefaultCalico returns sane defaults for calico
Expand All @@ -63,7 +60,6 @@ func DefaultCalico() *Calico {
MTU: 0,
EnableWireguard: false,
FlexVolumeDriverPath: "/usr/libexec/k0s/kubelet-plugins/volume/exec/nodeagent~uds",
WithWindowsNodes: false,
Overlay: "Always",
IPAutodetectionMethod: "",
IPv6AutodetectionMethod: "",
Expand All @@ -77,7 +73,6 @@ func (c *Calico) UnmarshalJSON(data []byte) error {
c.VxlanVNI = 4096
c.MTU = 1450
c.EnableWireguard = false
c.WithWindowsNodes = false
c.FlexVolumeDriverPath = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/nodeagent~uds"
c.Overlay = "Always"
c.IPAutodetectionMethod = ""
Expand Down
5 changes: 2 additions & 3 deletions pkg/autopilot/common/hostname.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package common
import (
"os"

nodeutil "k8s.io/component-helpers/node/util"
"github.com/k0sproject/k0s/pkg/node"
)

const (
Expand All @@ -28,6 +28,5 @@ const (
// for an AUTOPILOT_HOSTNAME environment variable, falling back to whatever the OS
// returns.
func FindEffectiveHostname() (string, error) {
// nodeutil.GetHostname will return the "object name safe" hostname, overridden via the env var if non-empty value
return nodeutil.GetHostname(os.Getenv(envAutopilotHostname))
return node.GetNodename(os.Getenv(envAutopilotHostname))
}
4 changes: 1 addition & 3 deletions pkg/component/controller/calico.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ type calicoConfig struct {
ClusterCIDRIPv4 string
ClusterCIDRIPv6 string
EnableWireguard bool
WithWindowsNodes bool
FlexVolumeDriverPath string
DualStack bool
EnvVars map[string]string
Expand Down Expand Up @@ -164,7 +163,7 @@ func (c *Calico) processConfigChanges(newConfig calicoConfig) error {
output := bytes.NewBuffer([]byte{})
contents, err := static.Asset(fmt.Sprintf("manifests/calico/%s/%s", dir, filename))
if err != nil {
return fmt.Errorf("failed to unpack manifest %s: %w", filename, err)
return fmt.Errorf("can't find manifest %s: %w", manifestName, err)
}

tw := templatewriter.TemplateWriter{
Expand Down Expand Up @@ -199,7 +198,6 @@ func (c *Calico) getConfig(clusterConfig *v1beta1.ClusterConfig) (calicoConfig,
CalicoCNIImage: clusterConfig.Spec.Images.Calico.CNI.URI(),
CalicoNodeImage: clusterConfig.Spec.Images.Calico.Node.URI(),
CalicoKubeControllersImage: clusterConfig.Spec.Images.Calico.KubeControllers.URI(),
WithWindowsNodes: clusterConfig.Spec.Network.Calico.WithWindowsNodes,
Overlay: clusterConfig.Spec.Network.Calico.Overlay,
IPAutodetectionMethod: clusterConfig.Spec.Network.Calico.IPAutodetectionMethod,
IPV6AutodetectionMethod: ipv6AutoDetectionMethod,
Expand Down
5 changes: 2 additions & 3 deletions pkg/component/controller/controllersleasecounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import (
"github.com/k0sproject/k0s/pkg/component/manager"
kubeutil "github.com/k0sproject/k0s/pkg/kubernetes"
"github.com/k0sproject/k0s/pkg/leaderelection"

nodeutil "k8s.io/component-helpers/node/util"
"github.com/k0sproject/k0s/pkg/node"

"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -58,7 +57,7 @@ func (l *K0sControllersLeaseCounter) Start(ctx context.Context) error {

// hostname used to make the lease names be clear to which controller they belong to
// follow kubelet convention for naming so we e.g. use lowercase hostname etc.
holderIdentity, err := nodeutil.GetHostname("")
holderIdentity, err := node.GetNodename("")
if err != nil {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/component/controller/extensions_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func (ec *ExtensionsController) Init(_ context.Context) error {
return nil
}

// Run
// Start
func (ec *ExtensionsController) Start(ctx context.Context) error {
clientConfig, err := clientcmd.BuildConfigFromFlags("", ec.kubeConfig)
if err != nil {
Expand Down
211 changes: 211 additions & 0 deletions pkg/component/controller/windowsstackcomponent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
Copyright 2023 k0s authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
"bytes"
"context"
"fmt"
"path/filepath"
"reflect"
"strings"
"time"

"github.com/k0sproject/k0s/internal/pkg/templatewriter"
"github.com/k0sproject/k0s/static"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1"
"github.com/k0sproject/k0s/pkg/config"
"github.com/k0sproject/k0s/pkg/constant"
k8sutil "github.com/k0sproject/k0s/pkg/kubernetes"
)

// WindowsStackComponent implements the component interface
// controller unpacks windows manifests
// if windows nodes are present in the cluster
type WindowsStackComponent struct {
log logrus.FieldLogger

kubeClientFactory k8sutil.ClientFactoryInterface
k0sVars *config.CfgVars
saver manifestsSaver
prevRenderingContext windowsStackRenderingContext
}

type windowsStackRenderingContext struct {
CNIBin string
CNIConf string
Mode string
KubeAPIHost string
KubeAPIPort string
IPv4ServiceCIDR string
Nameserver string
NodeImage string

KubeProxyImage string
KubeProxyVersion string
}

// NewWindowsStackComponent creates new WindowsStackComponent reconciler
func NewWindowsStackComponent(k0sVars *config.CfgVars, clientFactory k8sutil.ClientFactoryInterface, saver manifestsSaver) *WindowsStackComponent {
return &WindowsStackComponent{
log: logrus.WithFields(logrus.Fields{"component": "WindowsNodeController"}),
saver: saver,
kubeClientFactory: clientFactory,
k0sVars: k0sVars,
}
}

// Init no-op
func (n *WindowsStackComponent) Init(_ context.Context) error {
return nil
}

// Run checks and adds labels
func (n *WindowsStackComponent) Start(ctx context.Context) error {

go func() {
timer := time.NewTicker(1 * time.Minute)
defer timer.Stop()
for {
select {
case <-ctx.Done():
return
case <-timer.C:
if err := n.handleWindowsNode(ctx, n.prevRenderingContext); err != nil {
n.log.Errorf("failed to handle windows node: %v", err)
}
}
}
}()

return nil
}

func (n *WindowsStackComponent) handleWindowsNode(ctx context.Context, cfg windowsStackRenderingContext) error {
client, err := n.kubeClientFactory.GetClient()
if err != nil {
return fmt.Errorf("failed to get kube client: %v", err)
}
nodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{
LabelSelector: "kubernetes.io/os=windows",
})
if err != nil {
n.log.Errorf("failed to get node list: %v", err)
return fmt.Errorf("failed to get node list: %v", err)
}

if len(nodes.Items) == 0 {
// TODO: may be delete windows stack if it exists
return nil
}

n.log.Infof("found %d windows nodes", len(nodes.Items))
if err := n.createWindowsStack(n.prevRenderingContext); err != nil {
n.log.Errorf("failed to create windows stack: %v", err)
return fmt.Errorf("failed to create windows stack: %v", err)
}
n.log.Infof("successfully created windows stack")
return nil
}

func (n *WindowsStackComponent) Reconcile(_ context.Context, cfg *v1beta1.ClusterConfig) error {
if cfg.Spec.Network.Provider != "calico" {
return nil
}

existingCNI := existingCNIProvider(n.k0sVars.ManifestsDir)
if existingCNI != "" && existingCNI != constant.CNIProviderCalico {
return nil
}
newConfig, err := n.makeRenderingContext(cfg)
if err != nil {
return fmt.Errorf("failed to make calico rendering context: %v", err)
}
if !reflect.DeepEqual(newConfig, n.prevRenderingContext) {
n.prevRenderingContext = newConfig
}

return nil
}
func (n *WindowsStackComponent) makeRenderingContext(cfg *v1beta1.ClusterConfig) (windowsStackRenderingContext, error) {
dns, err := cfg.Spec.Network.DNSAddress()
if err != nil {
return windowsStackRenderingContext{}, fmt.Errorf("failed to parse dns address: %v", err)
}

return windowsStackRenderingContext{
// template rendering unescapes double backslashes
CNIBin: "c:\\\\opt\\\\cni\\\\bin",
CNIConf: "c:\\\\opt\\\\cni\\\\conf",
Mode: cfg.Spec.Network.Calico.Mode,
KubeAPIHost: cfg.Spec.API.Address,
KubeAPIPort: fmt.Sprintf("%d", cfg.Spec.API.Port),
IPv4ServiceCIDR: cfg.Spec.Network.ServiceCIDR,
Nameserver: dns,
NodeImage: "calico/windows:v3.23.5",
KubeProxyImage: "sigwindowstools/kube-proxy",
KubeProxyVersion: "v1.27.1-calico-hostprocess",
}, nil
}

// Stop no-op
func (n *WindowsStackComponent) Stop() error {
return nil
}

// createWindowsStack creates windows stack

func (n *WindowsStackComponent) createWindowsStack(newConfig windowsStackRenderingContext) error {
manifestDirectories, err := static.AssetDir("manifests/windows")
if err != nil {
return fmt.Errorf("error retrieving manifests: %v", err)
}
for _, dir := range manifestDirectories {
manifestPaths, err := static.AssetDir(fmt.Sprintf("manifests/windows/%s", dir))
if err != nil {
return fmt.Errorf("error retrieving manifests: %s. will retry", err.Error())
}
tryAndLog := func(name string, e error) {
n.log.Debugf("writing manifest %s", name)
if e != nil {
n.log.Errorf("failed to write manifest %s: %v, will re-try", name, e)
}
}

for _, filename := range manifestPaths {
manifestName := fmt.Sprintf("windows-%s-%s", dir, filename)
output := bytes.NewBuffer([]byte{})
n.log.Debugf("Reading manifest template %s", manifestName)
contents, err := static.Asset(fmt.Sprintf("manifests/windows/%s/%s", dir, filename))
if err != nil {
return fmt.Errorf("can't unpack manifest %s: %v", manifestName, err)
}

tw := templatewriter.TemplateWriter{
Name: fmt.Sprintf("windows-%s-%s", dir, strings.TrimSuffix(filename, filepath.Ext(filename))),
Template: string(contents),
Data: newConfig,
}
tryAndLog(manifestName, tw.WriteToBuffer(output))
tryAndLog(manifestName, n.saver.Save(manifestName, output.Bytes()))
}
}
return nil
}
Loading

0 comments on commit 25ba1f5

Please sign in to comment.