Skip to content

Commit

Permalink
feat: Provide operating system from config (#1809)
Browse files Browse the repository at this point in the history
* feat: Provide operating system from config

* validate provided operating system
* fallback to runtime detection

Signed-off-by: Artiom Diomin <kron82@gmail.com>

* Fix mistake with a OS detection

Signed-off-by: Artiom Diomin <kron82@gmail.com>

* Pre-pull images only on control-plane

Signed-off-by: Artiom Diomin <kron82@gmail.com>

* Set operating_system in terraform output for static workers as well

Signed-off-by: Artiom Diomin <kron82@gmail.com>

* Switch AWS terraform example from CentOS8 image to RockyLinux8

Signed-off-by: Artiom Diomin <kron82@gmail.com>
  • Loading branch information
kron4eg authored Feb 10, 2022
1 parent 7821186 commit 13b4099
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 14 deletions.
3 changes: 2 additions & 1 deletion docs/api_reference/v1beta2.en.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
+++
title = "v1beta2 API Reference"
date = 2022-01-28T16:22:51+05:00
date = 2022-02-04T17:38:06+02:00
weight = 11
+++
## v1beta2
Expand Down Expand Up @@ -401,6 +401,7 @@ HostConfig describes a single control plane node.
| isLeader | IsLeader indicates this host as a session leader. Default value is populated at the runtime. | bool | false |
| taints | Taints if not provided (i.e. nil) defaults to TaintEffectNoSchedule, with key node-role.kubernetes.io/master for control plane nodes. Explicitly empty (i.e. []corev1.Taint{}) means no taints will be applied (this is default for worker nodes). | [][corev1.Taint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#taint-v1-core) | false |
| kubelet | Kubelet | [KubeletConfig](#kubeletconfig) | false |
| operatingSystem | OperatingSystem information, can be populated at the runtime. | OperatingSystemName | false |

[Back to Group](#v1beta2)

Expand Down
2 changes: 2 additions & 0 deletions examples/terraform/aws/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ output "kubeone_hosts" {
cloud_provider = "aws"
private_address = aws_instance.control_plane.*.private_ip
hostnames = aws_instance.control_plane.*.private_dns
operating_system = var.os
ssh_agent_socket = var.ssh_agent_socket
ssh_port = var.ssh_port
ssh_private_key_file = var.ssh_private_key_file
Expand All @@ -54,6 +55,7 @@ output "kubeone_static_workers" {
workers1 = {
private_address = aws_instance.static_workers1.*.private_ip
hostnames = aws_instance.static_workers1.*.private_dns
operating_system = var.os
ssh_agent_socket = var.ssh_agent_socket
ssh_port = var.ssh_port
ssh_private_key_file = var.ssh_private_key_file
Expand Down
6 changes: 3 additions & 3 deletions examples/terraform/aws/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ variable "ami_filters" {
}

centos = {
owners = ["125523088429"] # CentOS
image_name = ["CentOS 8.* x86_64"]
osp_name = "osp-centos8"
owners = ["792107900819"] # RockyLinux
image_name = ["Rocky-8-ec2-*.x86_64"]
osp_name = "osp-centos"
}

flatcar = {
Expand Down
27 changes: 25 additions & 2 deletions pkg/apis/kubeone/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,31 @@ func (h *HostConfig) SetHostname(hostname string) {
}

// SetOperatingSystem sets the operating system for the given host
func (h *HostConfig) SetOperatingSystem(os OperatingSystemName) {
h.OperatingSystem = os
func (h *HostConfig) SetOperatingSystem(os OperatingSystemName) error {
if h.OperatingSystem.IsValid() {
h.OperatingSystem = os

return nil
}

return errors.Errorf("unknown operating system %q", os)
}

func (osName OperatingSystemName) IsValid() bool {
// linter exhaustive will make sure this switch is iterating over all current and future possibilities
switch osName {
case OperatingSystemNameUbuntu:
case OperatingSystemNameDebian:
case OperatingSystemNameCentOS:
case OperatingSystemNameRHEL:
case OperatingSystemNameAmazon:
case OperatingSystemNameFlatcar:
case OperatingSystemNameUnknown:
default:
return false
}

return true
}

// SetLeader sets is the given host leader
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/kubeone/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ type HostConfig struct {
Taints []corev1.Taint `json:"taints,omitempty"`
// Kubelet
Kubelet KubeletConfig `json:"kubelet,omitempty"`
// OperatingSystem information populated at the runtime.
OperatingSystem OperatingSystemName `json:"-"`
// OperatingSystem information, can be populated at the runtime.
OperatingSystem OperatingSystemName `json:"operatingSystem,omitempty"`
}

// ControlPlaneConfig defines control plane nodes
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/kubeone/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ type HostConfig struct {
Taints []corev1.Taint `json:"taints,omitempty"`
// Kubelet
Kubelet KubeletConfig `json:"kubelet,omitempty"`
// OperatingSystem information populated at the runtime.
OperatingSystem OperatingSystemName `json:"-"`
// OperatingSystem information, can be populated at the runtime.
OperatingSystem OperatingSystemName `json:"operatingSystem,omitempty"`
}

// ControlPlaneConfig defines control plane nodes
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/kubeone/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ func ValidateHostConfig(hosts []kubeoneapi.HostConfig, fldPath *field.Path) fiel
if len(h.SSHUsername) == 0 {
allErrs = append(allErrs, field.Required(fldPath, "no SSH username given"))
}
if !h.OperatingSystem.IsValid() {
allErrs = append(allErrs, field.Invalid(fldPath.Child("operatingSystem"), h.OperatingSystem, "invalid operatingSystem provided"))
}
}

return allErrs
Expand Down
28 changes: 28 additions & 0 deletions pkg/apis/kubeone/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1852,6 +1852,34 @@ func TestValidateHostConfig(t *testing.T) {
},
expectedError: true,
},
{
name: "valid OS",
hostConfig: []kubeoneapi.HostConfig{
{
PublicAddress: "192.168.1.1",
PrivateAddress: "192.168.0.1",
SSHPrivateKeyFile: "test",
SSHAgentSocket: "test",
SSHUsername: "root",
OperatingSystem: kubeoneapi.OperatingSystemNameCentOS,
},
},
expectedError: false,
},
{
name: "invalid OS",
hostConfig: []kubeoneapi.HostConfig{
{
PublicAddress: "192.168.1.1",
PrivateAddress: "192.168.0.1",
SSHPrivateKeyFile: "test",
SSHAgentSocket: "test",
SSHUsername: "root",
OperatingSystem: kubeoneapi.OperatingSystemName("non-existing"),
},
},
expectedError: true,
},
}

for _, tc := range tests {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func runApplyInstall(s *state.State, opts *applyOpts) error { // Print the expec

for _, node := range s.LiveCluster.StaticWorkers {
if !node.IsInCluster {
fmt.Printf("\t+ join worker node %q (%s)\n", node.Config.Hostname, node.Config.PrivateAddress)
fmt.Printf("\t+ join static worker node %q (%s)\n", node.Config.Hostname, node.Config.PrivateAddress)
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/tasks/prerequisites.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func installPrerequisites(s *state.State) error {
return fmt.Errorf("failed to install prerequisites: %w", err)
}

return s.RunTaskOnAllNodes(func(ctx *state.State, node *kubeoneapi.HostConfig, conn ssh.Connection) error {
return s.RunTaskOnControlPlane(func(ctx *state.State, node *kubeoneapi.HostConfig, conn ssh.Connection) error {
ctx.Logger.Info("Pre-pull images")

_, _, err := ctx.Runner.Run(
Expand Down
13 changes: 11 additions & 2 deletions pkg/tasks/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func determineHostname(s *state.State) error {

return s.RunTaskOnAllNodes(func(s *state.State, node *kubeoneapi.HostConfig, conn ssh.Connection) error {
if node.Hostname != "" {
s.Logger.Debugf("Hostname is already set to %q", node.Hostname)

return nil
}

Expand All @@ -64,6 +66,7 @@ func determineHostname(s *state.State) error {
return err
}

s.Logger.Debugf("Hostname is detected: %q", stdout)
node.SetHostname(stdout)

return nil
Expand All @@ -74,15 +77,21 @@ func determineOS(s *state.State) error {
s.Logger.Infoln("Determine operating system...")

return s.RunTaskOnAllNodes(func(s *state.State, node *kubeoneapi.HostConfig, conn ssh.Connection) error {
if node.OperatingSystem != kubeoneapi.OperatingSystemNameUnknown {
s.Logger.Debugf("Operating system is already set to %q", node.OperatingSystem)

return nil
}

buf, err := fs.ReadFile(sshiofs.New(conn), "/etc/os-release")
if err != nil {
return err
}

osrData := osrelease.Parse(string(buf))
node.SetOperatingSystem(kubeoneapi.OperatingSystemName(osrData.ID))
s.Logger.Debugf("Operating system detected: %q", osrData.ID)

return nil
return node.SetOperatingSystem(kubeoneapi.OperatingSystemName(osrData.ID))
}, state.RunParallel)
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/terraform/v1beta2/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type hostsSpec struct {
PublicAddress []string `json:"public_address"`
PrivateAddress []string `json:"private_address"`
Hostnames []string `json:"hostnames"`
OperatingSystem string `json:"operating_system"`
SSHUser string `json:"ssh_user"`
SSHPort int `json:"ssh_port"`
SSHPrivateKeyFile string `json:"ssh_private_key_file"`
Expand Down Expand Up @@ -286,6 +287,7 @@ func newHostConfig(publicIP, privateIP, hostname string, hs *hostsSpec) kubeonev
BastionPort: hs.BastionPort,
BastionUser: hs.BastionUser,
Hostname: hostname,
OperatingSystem: kubeonev1beta2.OperatingSystemName(hs.OperatingSystem),
PrivateAddress: privateIP,
PublicAddress: publicIP,
SSHAgentSocket: hs.SSHAgentSocket,
Expand Down

0 comments on commit 13b4099

Please sign in to comment.