feat: allow Kubernetes version to be configured

This allows for users to specifify which version of Kubernetes to use.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-09-27 07:23:19 -07:00
parent 6ec5cb02cb
commit c44f7669e5
25 changed files with 157 additions and 88 deletions

View File

@ -25,6 +25,7 @@ import (
"github.com/talos-systems/talos/cmd/osctl/cmd/cluster/pkg/node" "github.com/talos-systems/talos/cmd/osctl/cmd/cluster/pkg/node"
"github.com/talos-systems/talos/cmd/osctl/pkg/client/config" "github.com/talos-systems/talos/cmd/osctl/pkg/client/config"
"github.com/talos-systems/talos/cmd/osctl/pkg/helpers" "github.com/talos-systems/talos/cmd/osctl/pkg/helpers"
"github.com/talos-systems/talos/pkg/constants"
"github.com/talos-systems/talos/pkg/userdata/v1alpha1/generate" "github.com/talos-systems/talos/pkg/userdata/v1alpha1/generate"
"github.com/talos-systems/talos/pkg/version" "github.com/talos-systems/talos/pkg/version"
) )
@ -105,7 +106,7 @@ func create() (err error) {
ips[i] = fmt.Sprintf(baseNetwork, i+2) ips[i] = fmt.Sprintf(baseNetwork, i+2)
} }
input, err := generate.NewInput(clusterName, ips) input, err := generate.NewInput(clusterName, ips, kubernetesVersion)
if err != nil { if err != nil {
return err return err
} }
@ -363,6 +364,7 @@ func init() {
clusterUpCmd.Flags().IntVar(&masters, "masters", 3, "the number of masters to create") clusterUpCmd.Flags().IntVar(&masters, "masters", 3, "the number of masters to create")
clusterUpCmd.Flags().StringVar(&clusterCpus, "cpus", "1.5", "the share of CPUs as fraction (each container)") clusterUpCmd.Flags().StringVar(&clusterCpus, "cpus", "1.5", "the share of CPUs as fraction (each container)")
clusterUpCmd.Flags().IntVar(&clusterMemory, "memory", 1024, "the limit on memory usage in MB (each container)") clusterUpCmd.Flags().IntVar(&clusterMemory, "memory", 1024, "the limit on memory usage in MB (each container)")
clusterUpCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run")
clusterCmd.PersistentFlags().StringVar(&clusterName, "name", "talos_default", "the name of the cluster") clusterCmd.PersistentFlags().StringVar(&clusterName, "name", "talos_default", "the name of the cluster")
clusterCmd.AddCommand(clusterUpCmd) clusterCmd.AddCommand(clusterUpCmd)
clusterCmd.AddCommand(clusterDownCmd) clusterCmd.AddCommand(clusterDownCmd)

View File

@ -18,6 +18,7 @@ import (
"github.com/talos-systems/talos/cmd/osctl/pkg/client/config" "github.com/talos-systems/talos/cmd/osctl/pkg/client/config"
"github.com/talos-systems/talos/cmd/osctl/pkg/helpers" "github.com/talos-systems/talos/cmd/osctl/pkg/helpers"
"github.com/talos-systems/talos/pkg/constants"
udv0 "github.com/talos-systems/talos/pkg/userdata" udv0 "github.com/talos-systems/talos/pkg/userdata"
udgenv0 "github.com/talos-systems/talos/pkg/userdata/generate" udgenv0 "github.com/talos-systems/talos/pkg/userdata/generate"
"github.com/talos-systems/talos/pkg/userdata/translate" "github.com/talos-systems/talos/pkg/userdata/translate"
@ -26,7 +27,10 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
var genVersion string var (
configVersion string
kubernetesVersion string
)
// configCmd represents the config command. // configCmd represents the config command.
var configCmd = &cobra.Command{ var configCmd = &cobra.Command{
@ -133,7 +137,7 @@ var configGenerateCmd = &cobra.Command{
if len(args) != 2 { if len(args) != 2 {
log.Fatal("expected a cluster name and comma delimited list of IP addresses") log.Fatal("expected a cluster name and comma delimited list of IP addresses")
} }
switch genVersion { switch configVersion {
case "v0": case "v0":
genV0Userdata(args) genV0Userdata(args)
case "v1alpha1": case "v1alpha1":
@ -143,7 +147,7 @@ var configGenerateCmd = &cobra.Command{
} }
func genV0Userdata(args []string) { func genV0Userdata(args []string) {
input, err := udgenv0.NewInput(args[0], strings.Split(args[1], ",")) input, err := udgenv0.NewInput(args[0], strings.Split(args[1], ","), kubernetesVersion)
if err != nil { if err != nil {
helpers.Fatalf("failed to generate PKI and tokens: %v", err) helpers.Fatalf("failed to generate PKI and tokens: %v", err)
} }
@ -206,7 +210,7 @@ func writeV0Userdata(input *udgenv0.Input, t udgenv0.Type, name string) (err err
} }
func genV1Alpha1Userdata(args []string) { func genV1Alpha1Userdata(args []string) {
input, err := udgenv1alpha1.NewInput(args[0], strings.Split(args[1], ",")) input, err := udgenv1alpha1.NewInput(args[0], strings.Split(args[1], ","), kubernetesVersion)
if err != nil { if err != nil {
helpers.Fatalf("failed to generate PKI and tokens: %v", err) helpers.Fatalf("failed to generate PKI and tokens: %v", err)
} }
@ -270,9 +274,11 @@ func writeV1Alpha1Userdata(input *udgenv1alpha1.Input, t udgenv1alpha1.Type, nam
if err = ud.Validate(); err != nil { if err = ud.Validate(); err != nil {
return err return err
} }
if err = ioutil.WriteFile(strings.ToLower(name)+".yaml", []byte(data), 0644); err != nil { if err = ioutil.WriteFile(strings.ToLower(name)+".yaml", []byte(data), 0644); err != nil {
return err return err
} }
return nil return nil
} }
@ -283,7 +289,8 @@ func init() {
configAddCmd.Flags().StringVar(&key, "key", "", "the path to the key") configAddCmd.Flags().StringVar(&key, "key", "", "the path to the key")
configGenerateCmd.Flags().StringSliceVar(&additionalSANs, "additional-sans", []string{}, "additional Subject-Alt-Names for the APIServer certificate") configGenerateCmd.Flags().StringSliceVar(&additionalSANs, "additional-sans", []string{}, "additional Subject-Alt-Names for the APIServer certificate")
configGenerateCmd.Flags().StringVar(&canonicalControlplaneEndpoint, "controlplane-endpoint", "", "the canonical controlplane endpoint (IP or DNS name) and optional port (defaults to 6443)") configGenerateCmd.Flags().StringVar(&canonicalControlplaneEndpoint, "controlplane-endpoint", "", "the canonical controlplane endpoint (IP or DNS name) and optional port (defaults to 6443)")
configGenerateCmd.Flags().StringVar(&genVersion, "version", "v0", "desired machine config version to generate") configGenerateCmd.Flags().StringVar(&configVersion, "version", "v0", "desired machine config version to generate")
configGenerateCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run")
helpers.Should(configGenerateCmd.Flags().MarkDeprecated("version", "the v0 version of machine config will be removed in the next version of Talos")) helpers.Should(configGenerateCmd.Flags().MarkDeprecated("version", "the v0 version of machine config will be removed in the next version of Talos"))
helpers.Should(configAddCmd.MarkFlagRequired("ca")) helpers.Should(configAddCmd.MarkFlagRequired("ca"))
helpers.Should(configAddCmd.MarkFlagRequired("crt")) helpers.Should(configAddCmd.MarkFlagRequired("crt"))

View File

@ -40,15 +40,22 @@ var installCmd = &cobra.Command{
platform, err := platform.NewPlatform() platform, err := platform.NewPlatform()
if err == nil { if err == nil {
data, err = platform.UserData() if platform.Name() != platformArg {
if err != nil { log.Println("platform mismatch")
log.Fatal(err) } else {
data, err = platform.UserData()
if err != nil {
log.Fatal(err)
}
} }
} else { }
if data == nil {
// Ignore userdata load errors, since it need not necessarily exist yet. // Ignore userdata load errors, since it need not necessarily exist yet.
log.Printf("failed to source userdata from platform; falling back to defaults: %v", err) log.Printf("failed to source userdata from platform; falling back to defaults: %v", err)
data = &userdata.UserData{ data = &userdata.UserData{
Install: &userdata.Install{}, KubernetesVersion: constants.DefaultKubernetesVersion,
Install: &userdata.Install{},
} }
} }

1
go.mod
View File

@ -8,6 +8,7 @@ require (
code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
github.com/beevik/ntp v0.2.0 github.com/beevik/ntp v0.2.0
github.com/blang/semver v3.5.0+incompatible
github.com/containerd/cgroups v0.0.0-20190328223300-4994991857f9 github.com/containerd/cgroups v0.0.0-20190328223300-4994991857f9
github.com/containerd/containerd v1.2.7 github.com/containerd/containerd v1.2.7
github.com/containerd/continuity v0.0.0-20181003075958-be9bd761db19 // indirect github.com/containerd/continuity v0.0.0-20181003075958-be9bd761db19 // indirect

1
go.sum
View File

@ -50,6 +50,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradbeam/rtnetlink v0.0.0-20190820045831-7b9ca088b93d h1:w82c4zYkkMi98stDhWV5EB/+Pt6q8eyMzmtnzwy1HSs= github.com/bradbeam/rtnetlink v0.0.0-20190820045831-7b9ca088b93d h1:w82c4zYkkMi98stDhWV5EB/+Pt6q8eyMzmtnzwy1HSs=

View File

@ -56,8 +56,9 @@ func (k *Kubeadm) PreFunc(ctx context.Context, data *userdata.UserData) (err err
// Pull the image and unpack it. // Pull the image and unpack it.
containerdctx := namespaces.WithNamespace(ctx, "k8s.io") containerdctx := namespaces.WithNamespace(ctx, "k8s.io")
if _, err = client.Pull(containerdctx, constants.KubernetesImage, containerdapi.WithPullUnpack); err != nil { image := fmt.Sprintf("%s:v%s", constants.KubernetesImage, data.KubernetesVersion)
return fmt.Errorf("failed to pull image %q: %v", constants.KubernetesImage, err) if _, err = client.Pull(containerdctx, image, containerdapi.WithPullUnpack); err != nil {
return fmt.Errorf("failed to pull image %q: %v", image, err)
} }
return nil return nil
@ -82,7 +83,7 @@ func (k *Kubeadm) Condition(data *userdata.UserData) conditions.Condition {
// Runner implements the Service interface. // Runner implements the Service interface.
func (k *Kubeadm) Runner(data *userdata.UserData) (runner.Runner, error) { func (k *Kubeadm) Runner(data *userdata.UserData) (runner.Runner, error) {
image := constants.KubernetesImage image := fmt.Sprintf("%s:v%s", constants.KubernetesImage, data.KubernetesVersion)
// We only wan't to run kubeadm if it hasn't been ran already. // We only wan't to run kubeadm if it hasn't been ran already.
if _, err := os.Stat("/etc/kubernetes/kubelet.conf"); !os.IsNotExist(err) { if _, err := os.Stat("/etc/kubernetes/kubelet.conf"); !os.IsNotExist(err) {

View File

@ -95,7 +95,7 @@ func editClusterConfig(data *userdata.UserData) (err error) {
} }
// Hardcodes specific kubeadm config parameters // Hardcodes specific kubeadm config parameters
clusterConfiguration.KubernetesVersion = constants.KubernetesVersion clusterConfiguration.KubernetesVersion = data.KubernetesVersion
clusterConfiguration.UseHyperKubeImage = true clusterConfiguration.UseHyperKubeImage = true
// Apply CIS hardening recommendations; only generate encryption token only if we're the bootstrap node // Apply CIS hardening recommendations; only generate encryption token only if we're the bootstrap node

View File

@ -58,7 +58,7 @@ func (k *Kubelet) DependsOn(data *userdata.UserData) []string {
// Runner implements the Service interface. // Runner implements the Service interface.
func (k *Kubelet) Runner(data *userdata.UserData) (runner.Runner, error) { func (k *Kubelet) Runner(data *userdata.UserData) (runner.Runner, error) {
image := constants.KubernetesImage image := fmt.Sprintf("%s:v%s", constants.KubernetesImage, data.KubernetesVersion)
// Set the process arguments. // Set the process arguments.
args := runner.Args{ args := runner.Args{

View File

@ -105,11 +105,11 @@ const (
// KubeadmEtcdPeerKey is the path to the etcd CA private key. // KubeadmEtcdPeerKey is the path to the etcd CA private key.
KubeadmEtcdPeerKey = v1beta1.DefaultCertificatesDir + "/" + constants.EtcdPeerKeyName KubeadmEtcdPeerKey = v1beta1.DefaultCertificatesDir + "/" + constants.EtcdPeerKeyName
// KubernetesVersion is the enforced target version of the control plane. // DefaultKubernetesVersion is the default target version of the control plane.
KubernetesVersion = "v1.16.0" DefaultKubernetesVersion = "1.16.0"
// KubernetesImage is the enforced hyperkube image to use for the control plane. // KubernetesImage is the enforced hyperkube image to use for the control plane.
KubernetesImage = "k8s.gcr.io/hyperkube:" + KubernetesVersion KubernetesImage = "k8s.gcr.io/hyperkube"
// UserDataPath is the path to the downloaded user data. // UserDataPath is the path to the downloaded user data.
UserDataPath = "/run/userdata.yaml" UserDataPath = "/run/userdata.yaml"

View File

@ -110,6 +110,7 @@ machine:
install: {} install: {}
cluster: cluster:
controlPlane: controlPlane:
version: 1.16.0
ips: ips:
- 10.254.0.10 - 10.254.0.10
clusterName: spencer-test clusterName: spencer-test
@ -131,6 +132,7 @@ cluster:
// nolint: lll // nolint: lll
const testV0Config = `version: "" const testV0Config = `version: ""
kubernetesVersion: 1.16.0
security: security:
os: os:
ca: ca:

View File

@ -6,6 +6,7 @@ package generate
const controlPlaneTempl = `#!talos const controlPlaneTempl = `#!talos
version: "" version: ""
kubernetesVersion: {{ .KubernetesVersion }}
security: security:
os: os:
ca: ca:

View File

@ -19,10 +19,12 @@ import (
"text/template" "text/template"
"time" "time"
"gopkg.in/yaml.v2"
"github.com/talos-systems/talos/internal/pkg/cis" "github.com/talos-systems/talos/internal/pkg/cis"
"github.com/talos-systems/talos/pkg/constants"
"github.com/talos-systems/talos/pkg/crypto/x509" "github.com/talos-systems/talos/pkg/crypto/x509"
tnet "github.com/talos-systems/talos/pkg/net" tnet "github.com/talos-systems/talos/pkg/net"
"github.com/talos-systems/talos/pkg/userdata"
) )
// DefaultIPv4PodNet is the network to be used for kubernetes Pods when using IPv4-based master nodes // DefaultIPv4PodNet is the network to be used for kubernetes Pods when using IPv4-based master nodes
@ -214,7 +216,7 @@ func isIPv6(addrs ...string) bool {
// NewInput generates the sensitive data required to generate all userdata // NewInput generates the sensitive data required to generate all userdata
// types. // types.
// nolint: dupl,gocyclo // nolint: dupl,gocyclo
func NewInput(clustername string, masterIPs []string) (input *Input, err error) { func NewInput(clustername string, masterIPs []string, kubernetesVersion string) (input *Input, err error) {
var loopbackIP, podNet, serviceNet string var loopbackIP, podNet, serviceNet string
if isIPv6(masterIPs...) { if isIPv6(masterIPs...) {
@ -348,7 +350,7 @@ func NewInput(clustername string, masterIPs []string) (input *Input, err error)
ServiceNet: []string{serviceNet}, ServiceNet: []string{serviceNet},
ServiceDomain: "cluster.local", ServiceDomain: "cluster.local",
ClusterName: clustername, ClusterName: clustername,
KubernetesVersion: constants.KubernetesVersion, KubernetesVersion: kubernetesVersion,
KubeadmTokens: kubeadmTokens, KubeadmTokens: kubeadmTokens,
TrustdInfo: trustdInfo, TrustdInfo: trustdInfo,
} }
@ -399,24 +401,14 @@ func Userdata(t Type, in *Input) (string, error) {
return "", err return "", err
} }
// TODO: We cant implement this currently because of data := &userdata.UserData{}
// issues with kubeadm dependency mismatch between if err = yaml.Unmarshal([]byte(ud), data); err != nil {
// talos and clusterapi//kubebuilder. return "", err
// We should figure out way we can work around/through }
// this
/*
// Create an actual userdata struct from the
// generated data so we can call validate
// and ensure we are providing proper data
data := &userdata.UserData{}
if err = yaml.Unmarshal([]byte(ud), data); err != nil {
return "", err
}
if err = data.Validate(); err != nil { if err = data.Validate(); err != nil {
return "", err return "", err
} }
*/
return ud, nil return ud, nil
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/talos-systems/talos/pkg/constants"
"github.com/talos-systems/talos/pkg/userdata" "github.com/talos-systems/talos/pkg/userdata"
"github.com/talos-systems/talos/pkg/userdata/generate" "github.com/talos-systems/talos/pkg/userdata/generate"
) )
@ -31,10 +32,10 @@ func TestGenerateSuite(t *testing.T) {
func (suite *GenerateSuite) SetupSuite() { func (suite *GenerateSuite) SetupSuite() {
var err error var err error
input, err = generate.NewInput("test", []string{"10.0.1.5", "10.0.1.6", "10.0.1.7"}) input, err = generate.NewInput("test", []string{"10.0.1.5", "10.0.1.6", "10.0.1.7"}, constants.DefaultKubernetesVersion)
suite.Require().NoError(err) suite.Require().NoError(err)
inputv6, err = generate.NewInput("test", []string{"2001:db8::1", "2001:db8::2", "2001:db8::3"}) inputv6, err = generate.NewInput("test", []string{"2001:db8::1", "2001:db8::2", "2001:db8::3"}, constants.DefaultKubernetesVersion)
suite.Require().NoError(err) suite.Require().NoError(err)
} }

View File

@ -6,6 +6,7 @@ package generate
const initTempl = `#!talos const initTempl = `#!talos
version: "" version: ""
kubernetesVersion: {{ .KubernetesVersion }}
security: security:
os: os:
ca: ca:

View File

@ -6,6 +6,7 @@ package generate
const workerTempl = `#!talos const workerTempl = `#!talos
version: "" version: ""
kubernetesVersion: {{ .KubernetesVersion }}
security: null security: null
services: services:
init: init:

View File

@ -57,6 +57,10 @@ func CheckServices() ServiceCheck {
return func(s *Services) error { return func(s *Services) error {
var result *multierror.Error var result *multierror.Error
if s == nil {
return nil
}
if s.Kubeadm == nil { if s.Kubeadm == nil {
result = multierror.Append(result, xerrors.Errorf("[%s] %q: %w", "services.kubeadm", "", ErrRequiredSection)) result = multierror.Append(result, xerrors.Errorf("[%s] %q: %w", "services.kubeadm", "", ErrRequiredSection))
} }

View File

@ -43,6 +43,7 @@ machine:
install: {} install: {}
cluster: cluster:
controlPlane: controlPlane:
version: 1.16.0
ips: ips:
- 10.254.0.10 - 10.254.0.10
clusterName: spencer-test clusterName: spencer-test

View File

@ -15,7 +15,6 @@ import (
kubeletconfig "k8s.io/kubelet/config/v1beta1" kubeletconfig "k8s.io/kubelet/config/v1beta1"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
"github.com/talos-systems/talos/pkg/constants"
"github.com/talos-systems/talos/pkg/crypto/x509" "github.com/talos-systems/talos/pkg/crypto/x509"
"github.com/talos-systems/talos/pkg/userdata" "github.com/talos-systems/talos/pkg/userdata"
v1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1" v1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1"
@ -37,8 +36,9 @@ func (tv1a1 *V1Alpha1Translator) Translate() (*userdata.UserData, error) {
// Lay down the absolute minimum for all node types // Lay down the absolute minimum for all node types
ud := &userdata.UserData{ ud := &userdata.UserData{
Version: "v1alpha1", Version: "v1alpha1",
Security: &userdata.Security{}, KubernetesVersion: nc.Cluster.ControlPlane.Version,
Security: &userdata.Security{},
Services: &userdata.Services{ Services: &userdata.Services{
Init: &userdata.Init{ Init: &userdata.Init{
CNI: "flannel", CNI: "flannel",
@ -74,6 +74,11 @@ func (tv1a1 *V1Alpha1Translator) Translate() (*userdata.UserData, error) {
case "worker": case "worker":
translateV1Alpha1Worker(nc, ud) translateV1Alpha1Worker(nc, ud)
} }
if err = ud.Validate(); err != nil {
return nil, err
}
return ud, nil return ud, nil
} }
@ -218,7 +223,7 @@ func translateV1Alpha1Init(nc *v1alpha1.NodeConfig, ud *userdata.UserData) error
APIVersion: "kubeadm.k8s.io/v1beta2", APIVersion: "kubeadm.k8s.io/v1beta2",
}, },
ClusterName: nc.Cluster.ClusterName, ClusterName: nc.Cluster.ClusterName,
KubernetesVersion: constants.KubernetesVersion, KubernetesVersion: ud.KubernetesVersion,
ControlPlaneEndpoint: nc.Cluster.ControlPlane.IPs[0] + ":443", ControlPlaneEndpoint: nc.Cluster.ControlPlane.IPs[0] + ":443",
Networking: kubeadm.Networking{ Networking: kubeadm.Networking{
DNSDomain: nc.Cluster.Network.DNSDomain, DNSDomain: nc.Cluster.Network.DNSDomain,

View File

@ -11,24 +11,26 @@ import (
"os" "os"
"strings" "strings"
"github.com/blang/semver"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"golang.org/x/xerrors" "golang.org/x/xerrors"
yaml "gopkg.in/yaml.v2"
"github.com/talos-systems/talos/pkg/crypto/x509" "github.com/talos-systems/talos/pkg/crypto/x509"
yaml "gopkg.in/yaml.v2"
) )
// UserData represents the user data. // UserData represents the user data.
type UserData struct { type UserData struct {
Version Version `yaml:"version"` Version Version `yaml:"version"`
Security *Security `yaml:"security"` Security *Security `yaml:"security"`
Networking *Networking `yaml:"networking"` Networking *Networking `yaml:"networking"`
Services *Services `yaml:"services"` Services *Services `yaml:"services"`
Files []*File `yaml:"files"` Files []*File `yaml:"files"`
Debug bool `yaml:"debug"` Debug bool `yaml:"debug"`
Env Env `yaml:"env,omitempty"` Env Env `yaml:"env,omitempty"`
Install *Install `yaml:"install,omitempty"` Install *Install `yaml:"install,omitempty"`
KubernetesVersion string `yaml:"kubernetesVersion,omitempty"`
} }
// Validate ensures the required fields are present in the userdata // Validate ensures the required fields are present in the userdata
@ -36,10 +38,31 @@ type UserData struct {
func (data *UserData) Validate() error { func (data *UserData) Validate() error {
var result *multierror.Error var result *multierror.Error
if _, err := semver.Parse(data.KubernetesVersion); err != nil {
result = multierror.Append(result, errors.Wrap(err, "version must be semantic"))
}
// All nodeType checks // All nodeType checks
result = multierror.Append(result, data.Services.Validate(CheckServices())) if data.Services != nil {
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdAuth(), CheckTrustdEndpointsAreValidIPsOrHostnames())) result = multierror.Append(result, data.Services.Validate(CheckServices()))
result = multierror.Append(result, data.Services.Init.Validate(CheckInitCNI())) if data.Services.Trustd != nil {
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdAuth(), CheckTrustdEndpointsAreValidIPsOrHostnames()))
}
if data.Services.Init != nil {
result = multierror.Append(result, data.Services.Init.Validate(CheckInitCNI()))
}
if data.Services.Kubeadm != nil {
switch {
case data.Services.Kubeadm.IsBootstrap():
result = multierror.Append(result, data.Security.OS.Validate(CheckOSCA()))
result = multierror.Append(result, data.Security.Kubernetes.Validate(CheckKubernetesCA()))
case data.Services.Kubeadm.IsControlPlane():
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdEndpointsArePresent()))
case data.Services.Kubeadm.IsWorker():
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdEndpointsArePresent()))
}
}
}
// Surely there's a better way to do this // Surely there's a better way to do this
if data.Networking != nil && data.Networking.OS != nil { if data.Networking != nil && data.Networking.OS != nil {
@ -48,16 +71,6 @@ func (data *UserData) Validate() error {
} }
} }
switch {
case data.Services.Kubeadm.IsBootstrap():
result = multierror.Append(result, data.Security.OS.Validate(CheckOSCA()))
result = multierror.Append(result, data.Security.Kubernetes.Validate(CheckKubernetesCA()))
case data.Services.Kubeadm.IsControlPlane():
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdEndpointsArePresent()))
case data.Services.Kubeadm.IsWorker():
result = multierror.Append(result, data.Services.Trustd.Validate(CheckTrustdEndpointsArePresent()))
}
return result.ErrorOrNil() return result.ErrorOrNil()
} }

View File

@ -21,6 +21,7 @@ type ClusterConfig struct {
// ControlPlaneConfig represents control plane config vals // ControlPlaneConfig represents control plane config vals
type ControlPlaneConfig struct { type ControlPlaneConfig struct {
Version string `yaml:"version"`
// Endpoint is the canonical controlplane endpoint, which can be an IP // Endpoint is the canonical controlplane endpoint, which can be an IP
// address or a DNS hostname, is single-valued, and may optionally include a // address or a DNS hostname, is single-valued, and may optionally include a

View File

@ -25,8 +25,9 @@ func controlPlaneUd(in *Input) (string, error) {
cluster := &v1alpha1.ClusterConfig{ cluster := &v1alpha1.ClusterConfig{
Token: in.KubeadmTokens.BootstrapToken, Token: in.KubeadmTokens.BootstrapToken,
ControlPlane: &v1alpha1.ControlPlaneConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{
IPs: in.MasterIPs, Version: in.KubernetesVersion,
Index: in.Index, IPs: in.MasterIPs,
Index: in.Index,
}, },
CertificateKey: in.KubeadmTokens.CertificateKey, CertificateKey: in.KubeadmTokens.CertificateKey,
AESCBCEncryptionSecret: in.KubeadmTokens.AESCBCEncryptionSecret, AESCBCEncryptionSecret: in.KubeadmTokens.AESCBCEncryptionSecret,

View File

@ -18,9 +18,9 @@ import (
"time" "time"
"github.com/talos-systems/talos/internal/pkg/cis" "github.com/talos-systems/talos/internal/pkg/cis"
"github.com/talos-systems/talos/pkg/constants"
"github.com/talos-systems/talos/pkg/crypto/x509" "github.com/talos-systems/talos/pkg/crypto/x509"
tnet "github.com/talos-systems/talos/pkg/net" tnet "github.com/talos-systems/talos/pkg/net"
"github.com/talos-systems/talos/pkg/userdata/translate"
) )
// DefaultIPv4PodNet is the network to be used for kubernetes Pods when using IPv4-based master nodes // DefaultIPv4PodNet is the network to be used for kubernetes Pods when using IPv4-based master nodes
@ -227,7 +227,7 @@ func isIPv6(addrs ...string) bool {
// NewInput generates the sensitive data required to generate all userdata // NewInput generates the sensitive data required to generate all userdata
// types. // types.
// nolint: dupl,gocyclo // nolint: dupl,gocyclo
func NewInput(clustername string, masterIPs []string) (input *Input, err error) { func NewInput(clustername string, masterIPs []string, kubernetesVersion string) (input *Input, err error) {
var loopbackIP, podNet, serviceNet string var loopbackIP, podNet, serviceNet string
if isIPv6(masterIPs...) { if isIPv6(masterIPs...) {
@ -358,7 +358,7 @@ func NewInput(clustername string, masterIPs []string) (input *Input, err error)
ServiceNet: []string{serviceNet}, ServiceNet: []string{serviceNet},
ServiceDomain: "cluster.local", ServiceDomain: "cluster.local",
ClusterName: clustername, ClusterName: clustername,
KubernetesVersion: constants.KubernetesVersion, KubernetesVersion: kubernetesVersion,
KubeadmTokens: kubeadmTokens, KubeadmTokens: kubeadmTokens,
TrustdInfo: trustdInfo, TrustdInfo: trustdInfo,
} }
@ -388,15 +388,38 @@ func (t Type) String() string {
} }
// Userdata returns the talos userdata for a given node type. // Userdata returns the talos userdata for a given node type.
func Userdata(t Type, in *Input) (string, error) { // nolint: gocyclo
func Userdata(t Type, in *Input) (s string, err error) {
switch t { switch t {
case TypeInit: case TypeInit:
return initUd(in) if s, err = initUd(in); err != nil {
return "", err
}
case TypeControlPlane: case TypeControlPlane:
return controlPlaneUd(in) if s, err = controlPlaneUd(in); err != nil {
return "", err
}
case TypeJoin: case TypeJoin:
return workerUd(in) if s, err = workerUd(in); err != nil {
return "", err
}
default: default:
return "", errors.New("failed to determine userdata type to generate")
} }
return "", errors.New("failed to determine userdata type to generate")
trans, err := translate.NewTranslator("v1alpha1", s)
if err != nil {
return "", err
}
ud, err := trans.Translate()
if err != nil {
return "", err
}
if err = ud.Validate(); err != nil {
return "", err
}
return s, nil
} }

View File

@ -11,14 +11,15 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/talos-systems/talos/pkg/constants"
v1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1" v1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1"
udgenv1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1/generate" udgenv1alpha1 "github.com/talos-systems/talos/pkg/userdata/v1alpha1/generate"
) )
var input *udgenv1alpha1.Input
type GenerateSuite struct { type GenerateSuite struct {
suite.Suite suite.Suite
input *udgenv1alpha1.Input
} }
func TestGenerateSuite(t *testing.T) { func TestGenerateSuite(t *testing.T) {
@ -27,13 +28,13 @@ func TestGenerateSuite(t *testing.T) {
func (suite *GenerateSuite) SetupSuite() { func (suite *GenerateSuite) SetupSuite() {
var err error var err error
input, err = udgenv1alpha1.NewInput("test", []string{"10.0.1.5", "10.0.1.6", "10.0.1.7"}) suite.input, err = udgenv1alpha1.NewInput("test", []string{"10.0.1.5", "10.0.1.6", "10.0.1.7"}, constants.DefaultKubernetesVersion)
suite.Require().NoError(err) suite.Require().NoError(err)
} }
func (suite *GenerateSuite) TestGenerateInitSuccess() { func (suite *GenerateSuite) TestGenerateInitSuccess() {
input.IP = net.ParseIP("10.0.1.5") suite.input.IP = net.ParseIP("10.0.1.5")
dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeInit, input) dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeInit, suite.input)
suite.Require().NoError(err) suite.Require().NoError(err)
data := &v1alpha1.NodeConfig{} data := &v1alpha1.NodeConfig{}
err = yaml.Unmarshal([]byte(dataString), data) err = yaml.Unmarshal([]byte(dataString), data)
@ -41,8 +42,9 @@ func (suite *GenerateSuite) TestGenerateInitSuccess() {
} }
func (suite *GenerateSuite) TestGenerateControlPlaneSuccess() { func (suite *GenerateSuite) TestGenerateControlPlaneSuccess() {
input.IP = net.ParseIP("10.0.1.6") suite.input.IP = net.ParseIP("10.0.1.6")
dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeControlPlane, input) suite.input.Index = 1
dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeControlPlane, suite.input)
suite.Require().NoError(err) suite.Require().NoError(err)
data := &v1alpha1.NodeConfig{} data := &v1alpha1.NodeConfig{}
err = yaml.Unmarshal([]byte(dataString), data) err = yaml.Unmarshal([]byte(dataString), data)
@ -50,7 +52,7 @@ func (suite *GenerateSuite) TestGenerateControlPlaneSuccess() {
} }
func (suite *GenerateSuite) TestGenerateWorkerSuccess() { func (suite *GenerateSuite) TestGenerateWorkerSuccess() {
dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeJoin, input) dataString, err := udgenv1alpha1.Userdata(udgenv1alpha1.TypeJoin, suite.input)
suite.Require().NoError(err) suite.Require().NoError(err)
data := &v1alpha1.NodeConfig{} data := &v1alpha1.NodeConfig{}
err = yaml.Unmarshal([]byte(dataString), data) err = yaml.Unmarshal([]byte(dataString), data)
@ -58,6 +60,6 @@ func (suite *GenerateSuite) TestGenerateWorkerSuccess() {
} }
func (suite *GenerateSuite) TestGenerateTalosconfigSuccess() { func (suite *GenerateSuite) TestGenerateTalosconfigSuccess() {
_, err := udgenv1alpha1.Talosconfig(input) _, err := udgenv1alpha1.Talosconfig(suite.input)
suite.Require().NoError(err) suite.Require().NoError(err)
} }

View File

@ -27,6 +27,7 @@ func initUd(in *Input) (string, error) {
cluster := &v1alpha1.ClusterConfig{ cluster := &v1alpha1.ClusterConfig{
ClusterName: in.ClusterName, ClusterName: in.ClusterName,
ControlPlane: &v1alpha1.ControlPlaneConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{
Version: in.KubernetesVersion,
Endpoint: in.ControlPlaneEndpoint, Endpoint: in.ControlPlaneEndpoint,
IPs: in.MasterIPs, IPs: in.MasterIPs,
Index: in.Index, Index: in.Index,

View File

@ -21,7 +21,8 @@ func workerUd(in *Input) (string, error) {
cluster := &v1alpha1.ClusterConfig{ cluster := &v1alpha1.ClusterConfig{
Token: in.KubeadmTokens.BootstrapToken, Token: in.KubeadmTokens.BootstrapToken,
ControlPlane: &v1alpha1.ControlPlaneConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{
IPs: in.MasterIPs, Version: in.KubernetesVersion,
IPs: in.MasterIPs,
}, },
} }