terraform: openstack node groups (#1966)

* openstack

* rename to base_name

* fix openstack boot vtpm

* add docs for accessing bootstrapper logs

* rename to initial count
This commit is contained in:
Adrian Stobbe 2023-07-03 16:33:00 +02:00 committed by GitHub
parent d43242a55f
commit c39df2f7da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 125 deletions

View File

@ -171,7 +171,7 @@ func main() {
metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{},
) )
metadataAPI = metadata metadataAPI = metadata
openDevice = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
default: default:
clusterInitJoiner = &clusterFake{} clusterInitJoiner = &clusterFake{}

View File

@ -327,23 +327,32 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts
} }
vars := terraform.OpenStackClusterVariables{ vars := terraform.OpenStackClusterVariables{
CommonVariables: terraform.CommonVariables{
Name: opts.Config.Name, Name: opts.Config.Name,
CountControlPlanes: opts.ControlPlaneCount, Cloud: toPtr(opts.Config.Provider.OpenStack.Cloud),
CountWorkers: opts.WorkerCount,
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
},
Cloud: opts.Config.Provider.OpenStack.Cloud,
AvailabilityZone: opts.Config.Provider.OpenStack.AvailabilityZone,
FlavorID: opts.Config.Provider.OpenStack.FlavorID, FlavorID: opts.Config.Provider.OpenStack.FlavorID,
FloatingIPPoolID: opts.Config.Provider.OpenStack.FloatingIPPoolID, FloatingIPPoolID: opts.Config.Provider.OpenStack.FloatingIPPoolID,
StateDiskType: opts.Config.Provider.OpenStack.StateDiskType,
ImageURL: opts.image, ImageURL: opts.image,
DirectDownload: *opts.Config.Provider.OpenStack.DirectDownload, DirectDownload: *opts.Config.Provider.OpenStack.DirectDownload,
OpenstackUserDomainName: opts.Config.Provider.OpenStack.UserDomainName, OpenstackUserDomainName: opts.Config.Provider.OpenStack.UserDomainName,
OpenstackUsername: opts.Config.Provider.OpenStack.Username, OpenstackUsername: opts.Config.Provider.OpenStack.Username,
OpenstackPassword: opts.Config.Provider.OpenStack.Password, OpenstackPassword: opts.Config.Provider.OpenStack.Password,
Debug: opts.Config.IsDebugCluster(), Debug: opts.Config.IsDebugCluster(),
NodeGroups: map[string]terraform.OpenStackNodeGroup{
"control_plane_default": {
Role: role.ControlPlane.TFString(),
InitialCount: opts.ControlPlaneCount,
Zone: opts.Config.Provider.OpenStack.AvailabilityZone, // TODO(elchead): make configurable AB#3225
StateDiskType: opts.Config.Provider.OpenStack.StateDiskType,
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
},
"worker_default": {
Role: role.Worker.TFString(),
InitialCount: opts.WorkerCount,
Zone: opts.Config.Provider.OpenStack.AvailabilityZone, // TODO(elchead): make configurable AB#3225
StateDiskType: opts.Config.Provider.OpenStack.StateDiskType,
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
},
},
} }
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.OpenStack.String())), &vars); err != nil { if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.OpenStack.String())), &vars); err != nil {

View File

@ -158,40 +158,22 @@ resource "openstack_compute_secgroup_v2" "vpc_secgroup" {
} }
} }
module "instance_group_control_plane" { module "instance_group" {
source = "./modules/instance_group"
name = local.name
role = "control-plane"
initial_count = var.control_plane_count
image_id = openstack_images_image_v2.constellation_os_image.image_id
flavor_id = var.flavor_id
security_groups = [openstack_compute_secgroup_v2.vpc_secgroup.id]
tags = local.tags
uid = local.uid
disk_size = var.state_disk_size
state_disk_type = var.state_disk_type
availability_zone = var.availability_zone
network_id = openstack_networking_network_v2.vpc_network.id
init_secret_hash = local.initSecretHash
identity_internal_url = local.identity_internal_url
openstack_username = var.openstack_username
openstack_password = var.openstack_password
openstack_user_domain_name = var.openstack_user_domain_name
}
module "instance_group_worker" {
source = "./modules/instance_group" source = "./modules/instance_group"
name = local.name for_each = var.node_groups
role = "worker" base_name = local.name
initial_count = var.worker_count node_group_name = each.key
role = each.value.role
initial_count = each.value.initial_count
disk_size = each.value.state_disk_size
state_disk_type = each.value.state_disk_type
availability_zone = each.value.zone
image_id = openstack_images_image_v2.constellation_os_image.image_id image_id = openstack_images_image_v2.constellation_os_image.image_id
flavor_id = var.flavor_id flavor_id = var.flavor_id
security_groups = [openstack_compute_secgroup_v2.vpc_secgroup.id]
tags = local.tags tags = local.tags
uid = local.uid uid = local.uid
security_groups = [openstack_compute_secgroup_v2.vpc_secgroup.id]
disk_size = var.state_disk_size
state_disk_type = var.state_disk_type
availability_zone = var.availability_zone
network_id = openstack_networking_network_v2.vpc_network.id network_id = openstack_networking_network_v2.vpc_network.id
init_secret_hash = local.initSecretHash init_secret_hash = local.initSecretHash
identity_internal_url = local.identity_internal_url identity_internal_url = local.identity_internal_url
@ -209,15 +191,24 @@ resource "openstack_networking_floatingip_v2" "public_ip" {
resource "openstack_compute_floatingip_associate_v2" "public_ip_associate" { resource "openstack_compute_floatingip_associate_v2" "public_ip_associate" {
floating_ip = openstack_networking_floatingip_v2.public_ip.address floating_ip = openstack_networking_floatingip_v2.public_ip.address
instance_id = module.instance_group_control_plane.instance_ids.0 instance_id = module.instance_group["control_plane_default"].instance_ids.0
depends_on = [ depends_on = [
openstack_networking_router_v2.vpc_router, openstack_networking_router_v2.vpc_router,
openstack_networking_router_interface_v2.vpc_router_interface, openstack_networking_router_interface_v2.vpc_router_interface,
] ]
} }
# TODO(malt3): get LoadBalancer API enabled in the test environment moved {
from = module.instance_group_control_plane
to = module.instance_group["control_plane_default"]
}
moved {
from = module.instance_group_worker
to = module.instance_group["worker_default"]
}
# TODO(malt3): get LoadBalancer API enabled in the test environment
# resource "openstack_lb_loadbalancer_v2" "loadbalancer" { # resource "openstack_lb_loadbalancer_v2" "loadbalancer" {
# name = local.name # name = local.name
# description = "Constellation load balancer" # description = "Constellation load balancer"

View File

@ -8,8 +8,14 @@ terraform {
} }
locals { locals {
name = "${var.name}-${var.role}" tags = distinct(sort(concat(var.tags, ["constellation-role-${var.role}"], ["constellation-node-group-${var.node_group_name}"])))
tags = distinct(sort(concat(var.tags, ["constellation-role-${var.role}"]))) group_uid = random_id.uid.hex
#name = "${var.base_name}-${var.role}" // TODO keep old naming ?
name = "${var.base_name}-${var.role}-${local.group_uid}"
}
resource "random_id" "uid" {
byte_length = 4
} }
# TODO(malt3): get this API enabled in the test environment # TODO(malt3): get this API enabled in the test environment

View File

@ -1,4 +1,9 @@
variable "name" { variable "node_group_name" {
type = string
description = "Constellation name for the node group (used for configuration and CSP-independent naming)."
}
variable "base_name" {
type = string type = string
description = "Base name of the instance group." description = "Base name of the instance group."
} }

View File

@ -1,3 +1,20 @@
variable "node_groups" {
type = map(object({
role = string
initial_count = number // number of instances in the node group
state_disk_size = number // size of state disk (GiB)
state_disk_type = string // type of state disk. Can be 'standard' or 'premium'
zone = string // availability zone
}))
validation {
condition = can([for group in var.node_groups : group.role == "control-plane" || group.role == "worker"])
error_message = "The role has to be 'control-plane' or 'worker'."
}
description = "A map of node group names to node group configurations."
}
variable "cloud" { variable "cloud" {
type = string type = string
default = null default = null
@ -10,32 +27,6 @@ variable "name" {
description = "Base name of the cluster." description = "Base name of the cluster."
} }
variable "control_plane_count" {
type = number
description = "The number of control plane nodes to deploy."
}
variable "worker_count" {
type = number
description = "The number of worker nodes to deploy."
}
variable "state_disk_size" {
type = number
default = 30
description = "The size of the state disk in GB."
}
variable "state_disk_type" {
type = string
description = "Disk/volume type to be used."
}
variable "availability_zone" {
type = string
description = "The availability zone to deploy the nodes in."
}
variable "image_url" { variable "image_url" {
type = string type = string
description = "The image to use for cluster nodes." description = "The image to use for cluster nodes."

View File

@ -229,52 +229,49 @@ func (v *AzureIAMVariables) String() string {
// OpenStackClusterVariables is user configuration for creating a cluster with Terraform on OpenStack. // OpenStackClusterVariables is user configuration for creating a cluster with Terraform on OpenStack.
type OpenStackClusterVariables struct { type OpenStackClusterVariables struct {
// CommonVariables contains common variables. // Name of the cluster.
CommonVariables Name string `hcl:"name" cty:"name"`
// NodeGroups is a map of node groups to create.
NodeGroups map[string]OpenStackNodeGroup `hcl:"node_groups" cty:"node_groups"`
// Cloud is the (optional) name of the OpenStack cloud to use when reading the "clouds.yaml" configuration file. If empty, environment variables are used. // Cloud is the (optional) name of the OpenStack cloud to use when reading the "clouds.yaml" configuration file. If empty, environment variables are used.
Cloud string Cloud *string `hcl:"cloud" cty:"cloud"`
// AvailabilityZone is the OpenStack availability zone to use.
AvailabilityZone string
// Flavor is the ID of the OpenStack flavor (machine type) to use. // Flavor is the ID of the OpenStack flavor (machine type) to use.
FlavorID string FlavorID string `hcl:"flavor_id" cty:"flavor_id"`
// FloatingIPPoolID is the ID of the OpenStack floating IP pool to use for public IPs. // FloatingIPPoolID is the ID of the OpenStack floating IP pool to use for public IPs.
FloatingIPPoolID string FloatingIPPoolID string `hcl:"floating_ip_pool_id" cty:"floating_ip_pool_id"`
// StateDiskType is the OpenStack disk type to use for the state disk.
StateDiskType string
// ImageURL is the URL of the OpenStack image to use. // ImageURL is the URL of the OpenStack image to use.
ImageURL string ImageURL string `hcl:"image_url" cty:"image_url"`
// DirectDownload decides whether to download the image directly from the URL to OpenStack or to upload it from the local machine. // DirectDownload decides whether to download the image directly from the URL to OpenStack or to upload it from the local machine.
DirectDownload bool DirectDownload bool `hcl:"direct_download" cty:"direct_download"`
// OpenstackUserDomainName is the OpenStack user domain name to use. // OpenstackUserDomainName is the OpenStack user domain name to use.
OpenstackUserDomainName string OpenstackUserDomainName string `hcl:"openstack_user_domain_name" cty:"openstack_user_domain_name"`
// OpenstackUsername is the OpenStack user name to use. // OpenstackUsername is the OpenStack user name to use.
OpenstackUsername string OpenstackUsername string `hcl:"openstack_username" cty:"openstack_username"`
// OpenstackPassword is the OpenStack password to use. // OpenstackPassword is the OpenStack password to use.
OpenstackPassword string OpenstackPassword string `hcl:"openstack_password" cty:"openstack_password"`
// Debug is true if debug mode is enabled. // Debug is true if debug mode is enabled.
Debug bool Debug bool `hcl:"debug" cty:"debug"`
}
// OpenStackNodeGroup is a node group to create on OpenStack.
type OpenStackNodeGroup struct {
// Role is the role of the node group.
Role string `hcl:"role" cty:"role"`
// InitialCount is the number of instances to create.
InitialCount int `hcl:"initial_count" cty:"initial_count"`
// Zone is the OpenStack availability zone to use.
Zone string `hcl:"zone" cty:"zone"`
// StateDiskType is the OpenStack disk type to use for the state disk.
StateDiskType string `hcl:"state_disk_type" cty:"state_disk_type"`
// StateDiskSizeGB is the size of the state disk to allocate to each node, in GB.
StateDiskSizeGB int `hcl:"state_disk_size" cty:"state_disk_size"`
} }
// String returns a string representation of the variables, formatted as Terraform variables. // String returns a string representation of the variables, formatted as Terraform variables.
func (v *OpenStackClusterVariables) String() string { func (v *OpenStackClusterVariables) String() string {
b := &strings.Builder{} f := hclwrite.NewEmptyFile()
b.WriteString(v.CommonVariables.String()) gohcl.EncodeIntoBody(v, f.Body())
if v.Cloud != "" { return string(f.Bytes())
writeLinef(b, "cloud = %q", v.Cloud)
}
writeLinef(b, "availability_zone = %q", v.AvailabilityZone)
writeLinef(b, "flavor_id = %q", v.FlavorID)
writeLinef(b, "floating_ip_pool_id = %q", v.FloatingIPPoolID)
writeLinef(b, "image_url = %q", v.ImageURL)
writeLinef(b, "direct_download = %t", v.DirectDownload)
writeLinef(b, "state_disk_type = %q", v.StateDiskType)
writeLinef(b, "openstack_user_domain_name = %q", v.OpenstackUserDomainName)
writeLinef(b, "openstack_username = %q", v.OpenstackUsername)
writeLinef(b, "openstack_password = %q", v.OpenstackPassword)
writeLinef(b, "debug = %t", v.Debug)
return b.String()
} }
// TODO(malt3): Add support for OpenStack IAM variables. // TODO(malt3): Add support for OpenStack IAM variables.

View File

@ -229,37 +229,43 @@ resource_group_name = "my-resource-group"
func TestOpenStackClusterVariables(t *testing.T) { func TestOpenStackClusterVariables(t *testing.T) {
vars := OpenStackClusterVariables{ vars := OpenStackClusterVariables{
CommonVariables: CommonVariables{
Name: "cluster-name", Name: "cluster-name",
CountControlPlanes: 1, Cloud: toPtr("my-cloud"),
CountWorkers: 2,
StateDiskSizeGB: 30,
},
Cloud: "my-cloud",
AvailabilityZone: "az-01",
FlavorID: "flavor-0123456789abcdef", FlavorID: "flavor-0123456789abcdef",
FloatingIPPoolID: "fip-pool-0123456789abcdef", FloatingIPPoolID: "fip-pool-0123456789abcdef",
StateDiskType: "performance-8",
ImageURL: "https://example.com/image.raw", ImageURL: "https://example.com/image.raw",
DirectDownload: true, DirectDownload: true,
OpenstackUserDomainName: "my-user-domain", OpenstackUserDomainName: "my-user-domain",
OpenstackUsername: "my-username", OpenstackUsername: "my-username",
OpenstackPassword: "my-password", OpenstackPassword: "my-password",
Debug: true, Debug: true,
NodeGroups: map[string]OpenStackNodeGroup{
"control_plane_default": {
Role: "control-plane",
InitialCount: 1,
Zone: "az-01",
StateDiskType: "performance-8",
StateDiskSizeGB: 30,
},
},
} }
// test that the variables are correctly rendered // test that the variables are correctly rendered
want := `name = "cluster-name" want := `name = "cluster-name"
control_plane_count = 1 node_groups = {
worker_count = 2 control_plane_default = {
state_disk_size = 30 initial_count = 1
role = "control-plane"
state_disk_size = 30
state_disk_type = "performance-8"
zone = "az-01"
}
}
cloud = "my-cloud" cloud = "my-cloud"
availability_zone = "az-01"
flavor_id = "flavor-0123456789abcdef" flavor_id = "flavor-0123456789abcdef"
floating_ip_pool_id = "fip-pool-0123456789abcdef" floating_ip_pool_id = "fip-pool-0123456789abcdef"
image_url = "https://example.com/image.raw" image_url = "https://example.com/image.raw"
direct_download = true direct_download = true
state_disk_type = "performance-8"
openstack_user_domain_name = "my-user-domain" openstack_user_domain_name = "my-user-domain"
openstack_username = "my-username" openstack_username = "my-username"
openstack_password = "my-password" openstack_password = "my-password"

View File

@ -1,6 +1,9 @@
# Creating a Debug cluster # Debug cluster
A debug cluster allows quicker iteration cycles during development by being able to upload new bootstrapper binaries through the `cdbg` tool. A debug cluster allows quicker iteration cycles during development by being able to upload new bootstrapper binaries through the `cdbg` tool.
Furthermore, a debug cluster allows you to access the bootstrapper logs through the cloud providers serial console.
## Creating a debug cluster
After building (see [here](./build-develop-deploy.md#build)), you can find all CLIs and binaries in the `build` directory. After building (see [here](./build-develop-deploy.md#build)), you can find all CLIs and binaries in the `build` directory.
@ -44,3 +47,11 @@ Finally run:
```sh ```sh
./constellation init ./constellation init
``` ```
## Access bootstrapper logs
Once logged in to the control-plane machine, execute:
```sh
journalctl -fu constellation-bootstrapper
```